sequoia_tpm/
lib.rs

1#![deny(missing_docs)]
2//! Declarative TPM.
3//!
4//! This crate allows operating on TPM objects using declarative descriptions of keys
5//! and the associated artifacts.
6//!
7//! See binaries in `bin` for examples on how are these functions defined.
8
9use serde::{Deserialize, Serialize};
10use std::convert::{TryFrom, TryInto};
11use std::str::FromStr;
12use tss_esapi::attributes::object::ObjectAttributesBuilder;
13use tss_esapi::attributes::session::SessionAttributesBuilder;
14use tss_esapi::constants::session_type::SessionType;
15use tss_esapi::constants::tss::*;
16use tss_esapi::constants::CommandCode;
17use tss_esapi::handles::{KeyHandle, PersistentTpmHandle, TpmHandle};
18use tss_esapi::interface_types::algorithm::{
19    EccSchemeAlgorithm, PublicAlgorithm, RsaSchemeAlgorithm,
20};
21use tss_esapi::interface_types::ecc::EccCurve;
22use tss_esapi::interface_types::key_bits::RsaKeyBits;
23use tss_esapi::interface_types::resource_handles::Hierarchy;
24use tss_esapi::interface_types::session_handles::PolicySession;
25use tss_esapi::structures::SymmetricDefinitionObject;
26use tss_esapi::structures::{
27    Auth, Digest, EccParameter, EccScheme, KeyDerivationFunctionScheme, Private, PublicBuilder,
28    PublicEccParametersBuilder, PublicRsaParametersBuilder, RsaExponent, RsaScheme, Signature,
29    SignatureScheme,
30};
31use tss_esapi::Result;
32
33use tss_esapi::constants::tss::TPM2_ST_HASHCHECK;
34use tss_esapi::structures::{
35    Data, EccPoint, Public, PublicKeyRsa, RsaDecryptionScheme, SymmetricDefinition,
36};
37use tss_esapi::tss2_esys::TPMT_TK_HASHCHECK;
38use tss_esapi::{Context, Tcti};
39
40// Re-export as this is part of our public API.
41pub use tss_esapi::interface_types::algorithm::HashingAlgorithm;
42
43/// Description of a key.
44///
45/// The details of the key are specified by the `spec` field.
46#[derive(Debug, Serialize, Deserialize)]
47pub struct Description {
48    /// Specification of the described key.
49    pub spec: Specification,
50}
51
52/// Key details specification.
53///
54/// The specification of the contents of the key as well as by which provider it is managed.
55/// While this crate defines only the TPM provider other crates could define other provider
56/// types.
57#[derive(Debug, Serialize, Deserialize)]
58pub struct Specification {
59    /// Provider to use for the key.
60    pub provider: Provider,
61    /// Algorithm used by the key.
62    pub algo: AlgorithmSpec,
63    /// Optional: key private bits.
64    pub private: Option<PrivateKeyMaterial>,
65    /// List of key capabilities.
66    pub capabilities: Vec<Capability>,
67    /// Authentication value to use the key.
68    pub auth: String,
69}
70
71/// Key private material.
72///
73/// Defines data layout and contents of the private key definitions for both RSA and EC keys.
74#[derive(Debug, Serialize, Deserialize)]
75#[serde(rename_all = "lowercase")]
76pub enum PrivateKeyMaterial {
77    /// RSA key material.
78    Rsa(PrivateRsaKeyMaterial),
79    /// EC key material.
80    Ec(EcParameter),
81}
82
83/// RSA private key material.
84///
85/// Defines the contents of RSA private key: `prime` and `modulus` fields.
86#[derive(Debug, Serialize, Deserialize)]
87pub struct PrivateRsaKeyMaterial {
88    /// RSA prime factor.
89    pub prime: String,
90    /// RSA modulus factor.
91    pub modulus: RsaPublic,
92}
93
94impl From<&PrivateRsaKeyMaterial> for tss_esapi_sys::TPM2B_PRIVATE_KEY_RSA {
95    fn from(private_rsa: &PrivateRsaKeyMaterial) -> Self {
96        let key_prime = hex::decode(&private_rsa.prime).unwrap();
97        let mut result = Self::default();
98        result.buffer[..key_prime.len()].clone_from_slice(&key_prime[..key_prime.len()]);
99        result.size = key_prime.len().try_into().unwrap();
100        result
101    }
102}
103
104/// EC private key material.
105///
106/// Defines private key material for EC: `parameter` and both `points`.
107#[derive(Debug, Serialize, Deserialize)]
108pub struct EcParameter {
109    /// EC parameter.
110    pub parameter: String,
111    /// EC point.
112    pub points: EcPublic,
113}
114
115impl From<&EcParameter> for tss_esapi_sys::TPM2B_ECC_PARAMETER {
116    fn from(param: &EcParameter) -> Self {
117        let parameter = hex::decode(&param.parameter).unwrap();
118        let mut parameter_buffer = [0u8; 128];
119        parameter_buffer[..parameter.len()].clone_from_slice(&parameter);
120        Self {
121            size: parameter.len() as u16,
122            buffer: parameter_buffer,
123        }
124    }
125}
126
127/// Key capability.
128///
129/// Defines for which usages is this key intended. All keys need to be either `Decrypt` or `Sign`
130/// keys. Some keys can also be `Restrict`ed keys.
131///
132/// Keys that are both `Decrypt` and `Sign` are called storage keys and are used to load other
133/// keys in the TPM. Restricted decryption keys will not reveal the plaintext to non-TPM code.
134///
135/// Restricted signing keys will sign only digests produced in the TPM. They are used mostly
136/// to sign data produced by the TPM, e.g. the attestation data.
137#[derive(Debug, Serialize, Deserialize, PartialEq)]
138#[serde(rename_all = "lowercase")]
139pub enum Capability {
140    /// Allows decryption using this key.
141    Decrypt,
142    /// Allows signing using this key.
143    Sign,
144    /// This key is restricted.
145    Restrict,
146    /// This is an unknown, unsupported capability.
147    #[serde(other)]
148    Unknown,
149}
150
151/// Algorithm specification.
152///
153/// If a new key is about to be created the specifics of the algorithm need to be defined.
154/// This object specifies details for each supported asymmetric algorithm.
155#[derive(Debug, Serialize, Deserialize)]
156pub enum AlgorithmSpec {
157    /// RSA bits and optional exponent.
158    #[serde(rename = "RSA")]
159    Rsa {
160        /// Number of bits for this key.
161        bits: u16,
162        /// The exponent if it's different from 0x010001.
163        exponent: Option<u32>,
164    },
165    /// EC curve.
166    #[serde(rename = "EC")]
167    Ec {
168        /// Elliptic Curve to use.
169        curve: EcCurve,
170    },
171}
172
173/// EC curve.
174///
175/// EC curve that is supported by this library.
176#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
177pub enum EcCurve {
178    /// NIST P-256 curve.
179    #[serde(rename = "NIST-P256")]
180    NistP256,
181    /// NIST P-384 curve.
182    #[serde(rename = "NIST-P384")]
183    NistP384,
184}
185
186impl From<&EcCurve> for EccCurve {
187    fn from(curve: &EcCurve) -> Self {
188        match curve {
189            EcCurve::NistP256 => EccCurve::NistP256,
190            EcCurve::NistP384 => EccCurve::NistP384,
191        }
192    }
193}
194
195/// Cryptographic provider.
196///
197/// Specifies cryptographic provider that should be used for all cryptographic operations.
198#[derive(Debug, Serialize, Deserialize)]
199pub struct Provider {
200    /// Defines details for the TPM provider.
201    pub tpm: TpmProvider,
202}
203
204/// TPM cryptographic provider.
205///
206/// Specifies details of the target TPM as well as several TPM-specific key properties.
207#[derive(Debug, Serialize, Deserialize)]
208pub struct TpmProvider {
209    /// Connection string for the provider. E.g. `mssim:` for the simulator or
210    /// `device:/dev/tpmrm0` for a hardware device.
211    pub tcti: String,
212    /// Numeric handle of the key if it is persisted.
213    pub handle: Option<u32>,
214    /// Numeric handle of the parent of this key if the parent is persisted.
215    pub parent: Option<u32>,
216    /// Wrapped private bytes of the key.
217    pub private: Option<String>,
218    /// Wrapped unique/public bytes of the key.
219    pub unique: Option<PublicKeyBytes>,
220    /// Key has been wrapped by [`wrap`].
221    pub wrapped: Option<WrappedKey>,
222    /// Policy of key usage.
223    pub policy: Option<String>,
224}
225
226/// Key that has been wrapped.
227///
228/// Some cryptographic providers can export a wrapped (encrypted) key. This structure
229/// defines details of these keys.
230#[derive(Debug, Serialize, Deserialize)]
231pub struct WrappedKey {
232    /// Secret value that has been wrapped.
233    pub secret: String,
234    /// Private value that has been wrapped.
235    pub private: String,
236    /// Encrypted data for wrapping.
237    pub data: String,
238}
239
240/// Public key bits.
241///
242/// For export of public keys this structure is used with one variant per one supported
243/// asymmetric algorithm.
244#[derive(Debug, Serialize, Deserialize)]
245pub enum PublicKeyBytes {
246    /// RSA public key details.
247    RSA(RsaPublic),
248    /// EC public details.
249    EC(EcPublic),
250}
251
252/// RSA public key.
253///
254/// Defines raw `bytes` of the public key of the RSA key.
255#[derive(Debug, Serialize, Deserialize)]
256pub struct RsaPublic {
257    /// RSA raw bytes.
258    pub bytes: String,
259}
260
261impl From<&RsaPublic> for PublicKeyRsa {
262    fn from(public: &RsaPublic) -> Self {
263        let public_modulus = hex::decode(&public.bytes).unwrap();
264        PublicKeyRsa::try_from(public_modulus).unwrap()
265    }
266}
267
268/// EC public point.
269///
270/// Defines the coordinates of EC's public point.
271#[derive(Debug, Serialize, Deserialize)]
272pub struct EcPublic {
273    /// Point's `x` coordinate.
274    pub x: String,
275    /// Point's `y` coordinate.
276    pub y: String,
277}
278
279impl From<&EcPublic> for EccPoint {
280    fn from(public: &EcPublic) -> Self {
281        let x = hex::decode(&public.x).unwrap();
282        let y = hex::decode(&public.y).unwrap();
283
284        EccPoint::new(
285            EccParameter::try_from(x).unwrap(),
286            EccParameter::try_from(y).unwrap(),
287        )
288    }
289}
290
291/// Create key given by the `spec`.
292///
293/// Creates given key and returns the `PublicBuilder` for that key. If the key was a private key import
294/// this function additionally returns a Sensitive value.
295pub fn create(
296    spec: &Specification,
297) -> Result<(PublicBuilder, Option<tss_esapi_sys::TPM2B_SENSITIVE>)> {
298    let is_fixed = spec.private.is_none()
299        && spec.provider.tpm.wrapped.is_none()
300        && spec.provider.tpm.policy.is_none();
301    let attributes = ObjectAttributesBuilder::new()
302        .with_fixed_tpm(is_fixed)
303        .with_fixed_parent(is_fixed)
304        .with_sensitive_data_origin(true)
305        .with_user_with_auth(true)
306        .with_decrypt(spec.capabilities.contains(&Capability::Decrypt))
307        .with_sign_encrypt(spec.capabilities.contains(&Capability::Sign))
308        .with_restricted(spec.capabilities.contains(&Capability::Restrict))
309        .build()?;
310
311    let mut builder = PublicBuilder::new()
312        .with_name_hashing_algorithm(HashingAlgorithm::Sha256)
313        .with_object_attributes(attributes);
314
315    if let Some(policy) = &spec.provider.tpm.policy {
316        builder = builder.with_auth_policy(Digest::try_from(hex::decode(policy).unwrap())?);
317    }
318
319    if let Some(unique) = &spec.provider.tpm.unique {
320        builder = match unique {
321            PublicKeyBytes::RSA(ref bytes) => builder.with_rsa_unique_identifier(bytes.into()),
322            PublicKeyBytes::EC(ref bytes) => builder.with_ecc_unique_identifier(bytes.into()),
323        }
324    } else {
325        builder = builder
326            .with_rsa_unique_identifier(Default::default())
327            .with_ecc_unique_identifier(Default::default());
328    }
329
330    builder = match &spec.algo {
331        AlgorithmSpec::Rsa { bits, exponent } => {
332            let mut rsa_params_builder = PublicRsaParametersBuilder::new();
333            if spec.capabilities.contains(&Capability::Restrict) {
334                rsa_params_builder =
335                    rsa_params_builder.with_symmetric(SymmetricDefinitionObject::AES_256_CFB);
336            }
337            rsa_params_builder = rsa_params_builder
338                .with_scheme(if spec.capabilities.contains(&Capability::Decrypt) {
339                    RsaScheme::Null
340                } else if spec.capabilities.contains(&Capability::Sign) {
341                    RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
342                        .unwrap()
343                } else {
344                    panic!("Key needs to be for decryption or for signing")
345                })
346                .with_key_bits(RsaKeyBits::try_from(*bits).unwrap())
347                .with_exponent(RsaExponent::try_from(exponent.unwrap_or(0)).unwrap())
348                .with_is_signing_key(spec.capabilities.contains(&Capability::Sign))
349                .with_is_decryption_key(spec.capabilities.contains(&Capability::Decrypt))
350                .with_restricted(spec.capabilities.contains(&Capability::Restrict));
351
352            let rsa_params = rsa_params_builder.build()?;
353
354            builder
355                .with_public_algorithm(PublicAlgorithm::Rsa)
356                .with_rsa_parameters(rsa_params)
357        }
358        AlgorithmSpec::Ec { ref curve } => {
359            let mut ecc_builder = PublicEccParametersBuilder::new()
360                .with_ecc_scheme(if spec.capabilities.contains(&Capability::Decrypt) {
361                    EccScheme::Null
362                } else if spec.capabilities.contains(&Capability::Sign) {
363                    EccScheme::create(
364                        EccSchemeAlgorithm::EcDsa,
365                        Some(HashingAlgorithm::Sha256),
366                        None,
367                    )
368                    .unwrap()
369                } else {
370                    panic!("Key needs to be for decryption or for signing")
371                })
372                .with_curve(curve.into())
373                .with_is_signing_key(spec.capabilities.contains(&Capability::Sign))
374                .with_is_decryption_key(spec.capabilities.contains(&Capability::Decrypt))
375                .with_restricted(spec.capabilities.contains(&Capability::Restrict))
376                .with_key_derivation_function_scheme(KeyDerivationFunctionScheme::Null);
377            if spec.capabilities.contains(&Capability::Restrict) {
378                ecc_builder = ecc_builder.with_symmetric(SymmetricDefinitionObject::AES_256_CFB);
379            }
380
381            builder = builder
382                .with_public_algorithm(PublicAlgorithm::Ecc)
383                .with_ecc_parameters(ecc_builder.build()?);
384
385            builder
386        }
387    };
388
389    let private = match spec.private {
390        Some(PrivateKeyMaterial::Rsa(ref private_rsa)) => {
391            let rsa: tss_esapi_sys::TPM2B_PRIVATE_KEY_RSA = private_rsa.into();
392
393            builder = builder.with_rsa_unique_identifier((&private_rsa.modulus).into());
394            Some(tss_esapi_sys::TPM2B_SENSITIVE {
395                size: rsa.size,
396                sensitiveArea: tss_esapi_sys::TPMT_SENSITIVE {
397                    sensitiveType: TPM2_ALG_RSA,
398                    authValue: Default::default(),
399                    seedValue: Default::default(),
400                    sensitive: tss_esapi_sys::TPMU_SENSITIVE_COMPOSITE { rsa },
401                },
402            })
403        }
404        Some(PrivateKeyMaterial::Ec(ref param)) => {
405            let ecc: tss_esapi_sys::TPM2B_ECC_PARAMETER = param.into();
406            builder = builder.with_ecc_unique_identifier((&(param.points)).into());
407            Some(tss_esapi_sys::TPM2B_SENSITIVE {
408                size: ecc.size,
409                sensitiveArea: tss_esapi_sys::TPMT_SENSITIVE {
410                    sensitiveType: TPM2_ALG_ECC,
411                    authValue: Default::default(),
412                    seedValue: Default::default(),
413                    sensitive: tss_esapi_sys::TPMU_SENSITIVE_COMPOSITE { ecc },
414                },
415            })
416        }
417        _ => None,
418    };
419
420    Ok((builder, private))
421}
422
423/// Converts `specification` to raw key handle.
424///
425/// TPM functions require a key handle as a key identifier. This function converts the key
426/// specification to the internal key handle object.
427pub fn convert_to_key_handle(
428    context: &mut Context,
429    specification: &Specification,
430) -> Result<KeyHandle> {
431    let key_handle = if let (public, Some(private)) = create(specification)? {
432        context.load_external(private.try_into()?, public.build()?, Hierarchy::Null)?
433    } else if let Some(handle) = specification.provider.tpm.handle {
434        let persistent_tpm_handle = PersistentTpmHandle::new(handle)?;
435
436        let handle = context.execute_without_session(|ctx| {
437            ctx.tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
438                .expect("Need handle")
439        });
440
441        handle.into()
442    } else if let (Some(parent), Some(private)) = (
443        specification.provider.tpm.parent,
444        &specification.provider.tpm.private,
445    ) {
446        let persistent_tpm_handle = PersistentTpmHandle::new(parent)?;
447
448        let handle = context.execute_without_session(|ctx| {
449            ctx.tr_from_tpm_public(TpmHandle::Persistent(persistent_tpm_handle))
450                .expect("Need handle")
451        });
452
453        let key_handle: KeyHandle = handle.into();
454
455        context.tr_set_auth(
456            key_handle.into(),
457            Auth::try_from(specification.auth.as_bytes())?,
458        )?;
459
460        context.load(
461            key_handle,
462            Private::try_from(hex::decode(private).unwrap())?,
463            create(specification)?.0.build()?,
464        )?
465    } else {
466        panic!("Cannot load key");
467    };
468
469    context.tr_set_auth(
470        key_handle.into(),
471        Auth::try_from(specification.auth.as_bytes())?,
472    )?;
473
474    Ok(key_handle)
475}
476
477/// Read public key.
478///
479/// Refreshes the key specification with data available in the cryptographic provider
480/// most notably the public key.
481pub fn read_key(spec: &mut Specification) -> Result<()> {
482    let tcti = Tcti::from_str(&spec.provider.tpm.tcti)?;
483
484    let mut context = Context::new(tcti)?;
485
486    let session = context.start_auth_session(
487        None,
488        None,
489        None,
490        SessionType::Hmac,
491        SymmetricDefinition::AES_256_CFB,
492        HashingAlgorithm::Sha256,
493    )?;
494    let (session_attr, session_mask) = SessionAttributesBuilder::new()
495        .with_decrypt(true)
496        .with_encrypt(true)
497        .build();
498    context
499        .tr_sess_set_attributes(session.unwrap(), session_attr, session_mask)
500        .unwrap();
501    context.set_sessions((session, None, None));
502
503    let key_handle = convert_to_key_handle(&mut context, spec)?;
504
505    let (public, _, _) = context.read_public(key_handle)?;
506
507    let public_key = match &public {
508        Public::Rsa { unique, .. } => PublicKeyBytes::RSA(RsaPublic {
509            bytes: hex::encode(unique.value()),
510        }),
511        Public::Ecc { unique, .. } => PublicKeyBytes::EC(EcPublic {
512            x: hex::encode(unique.x().value()),
513            y: hex::encode(unique.y().value()),
514        }),
515        _ => panic!("Unsupported key type."),
516    };
517
518    spec.provider.tpm.unique = Some(public_key);
519    spec.provider.tpm.policy = hex::encode(
520        match &public {
521            tss_esapi::structures::Public::Rsa { auth_policy, .. } => auth_policy,
522            tss_esapi::structures::Public::Ecc { auth_policy, .. } => auth_policy,
523            _ => panic!("Unsupported key type."),
524        }
525        .value(),
526    )
527    .into();
528
529    Ok(())
530}
531
532/// Decrypt a buffer of data.
533///
534/// Use RSA decrypt function to decrypt a piece of `ciphertext` into a vector
535/// of bytes.
536///
537/// *Note*: for EC keys use [`derive`].
538pub fn decrypt(spec: &Specification, ciphertext: &[u8]) -> Result<Vec<u8>> {
539    let tcti = Tcti::from_str(&spec.provider.tpm.tcti)?;
540
541    let mut context = Context::new(tcti)?;
542
543    let session = context.start_auth_session(
544        None,
545        None,
546        None,
547        SessionType::Hmac,
548        SymmetricDefinition::AES_256_CFB,
549        HashingAlgorithm::Sha256,
550    )?;
551    let (session_attr, session_mask) = SessionAttributesBuilder::new()
552        .with_decrypt(true)
553        .with_encrypt(true)
554        .build();
555    context
556        .tr_sess_set_attributes(session.unwrap(), session_attr, session_mask)
557        .unwrap();
558    context.set_sessions((session, None, None));
559
560    let key_handle = convert_to_key_handle(&mut context, spec)?;
561
562    let cipher_text = PublicKeyRsa::try_from(ciphertext)?;
563
564    let plain_text = context.rsa_decrypt(
565        key_handle,
566        cipher_text,
567        RsaDecryptionScheme::RsaEs,
568        Data::default(),
569    )?;
570
571    Ok(plain_text.to_vec())
572}
573
574/// Derives a common point.
575///
576/// Use EC algorithm to derive a common point on a curve for both the key
577/// defined by `spec` and the public Z point defined in `data`.
578pub fn derive(spec: &Specification, data: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
579    let tcti = Tcti::from_str(&spec.provider.tpm.tcti)?;
580
581    let mut context = Context::new(tcti)?;
582
583    let session = context.start_auth_session(
584        None,
585        None,
586        None,
587        SessionType::Hmac,
588        SymmetricDefinition::AES_256_CFB,
589        HashingAlgorithm::Sha256,
590    )?;
591    let (session_attr, session_mask) = SessionAttributesBuilder::new()
592        .with_decrypt(true)
593        .with_encrypt(true)
594        .build();
595    context
596        .tr_sess_set_attributes(session.unwrap(), session_attr, session_mask)
597        .unwrap();
598    context.set_sessions((session, None, None));
599
600    let key_handle = convert_to_key_handle(&mut context, spec)?;
601
602    let size = data.len() / 2;
603
604    let z_point = context.ecdh_z_gen(
605        key_handle,
606        EccPoint::new(
607            EccParameter::try_from(&data[..size])?,
608            EccParameter::try_from(&data[size..])?,
609        ),
610    )?;
611
612    Ok((z_point.x().value().to_vec(), z_point.y().value().to_vec()))
613}
614
615/// Sign a hash of data.
616///
617/// Uses key defined by `spec` to sign a digest specified by `hash` and returns raw
618/// signatures bytes as a vector. For RSA the vector specifies raw signature values.
619/// For EC the vector is a concatenation of signature's R and S values.
620///
621/// Note: most TPMs only support SHA256.
622pub fn sign(spec: &Specification, hash_algo: HashingAlgorithm, hash: &[u8])
623    -> Result<Vec<u8>>
624{
625    let tcti = Tcti::from_str(&spec.provider.tpm.tcti)?;
626
627    let mut context = Context::new(tcti)?;
628
629    let session = context.start_auth_session(
630        None,
631        None,
632        None,
633        SessionType::Hmac,
634        SymmetricDefinition::AES_256_CFB,
635        hash_algo,
636    )?;
637    let (session_attr, session_mask) = SessionAttributesBuilder::new()
638        .with_decrypt(true)
639        .with_encrypt(true)
640        .build();
641    context
642        .tr_sess_set_attributes(session.unwrap(), session_attr, session_mask)
643        .unwrap();
644    context.set_sessions((session, None, None));
645
646    let key_handle = convert_to_key_handle(&mut context, spec)?;
647
648    let scheme = SignatureScheme::Null;
649    let validation = TPMT_TK_HASHCHECK {
650        tag: TPM2_ST_HASHCHECK,
651        hierarchy: TPM2_RH_NULL,
652        digest: Default::default(),
653    }
654    .try_into()?;
655
656    let digest = Digest::try_from(hash)?;
657
658    let signature = context.sign(key_handle, digest, scheme, validation)?;
659
660    Ok(match signature {
661        Signature::RsaSsa(ref signature) => Vec::from(signature.signature().value()),
662        Signature::EcDsa(signature) => {
663            let mut sig = vec![];
664            sig.extend(signature.signature_r().value());
665            sig.extend(signature.signature_s().value());
666            sig
667        }
668        _ => panic!("Unsupported signature scheme."),
669    })
670}
671
672/// Wrap (encrypt) a key.
673///
674/// Wraps the key specified by `spec` with the key specified by `parent`. Effectively this
675/// encrypts the private portion of the `spec` key with public key of the `parent` key.
676/// The `spec` key needs to allow wrapping (e.g. using PolicyDuplicationSelect) and the `parent`
677/// key needs to support import of wrapped keys (e.g. using Decrypt+Restricted capabilities).
678pub fn wrap(spec: &mut Specification, parent: &Specification) -> Result<()> {
679    let tcti = Tcti::from_str(&spec.provider.tpm.tcti)?;
680
681    let mut context = Context::new(tcti)?;
682
683    // create a policy digest that allows key duplication
684    let trial_session = context
685        .start_auth_session(
686            None,
687            None,
688            None,
689            SessionType::Trial,
690            SymmetricDefinition::AES_256_CFB,
691            HashingAlgorithm::Sha256,
692        )?
693        .expect("Start auth session returned a NONE handle");
694
695    let (policy_auth_session_attributes, policy_auth_session_attributes_mask) =
696        SessionAttributesBuilder::new()
697            .with_decrypt(true)
698            .with_encrypt(true)
699            .build();
700
701    context.tr_sess_set_attributes(
702        trial_session,
703        policy_auth_session_attributes,
704        policy_auth_session_attributes_mask,
705    )?;
706
707    let policy_session = PolicySession::try_from(trial_session)?;
708
709    context.policy_auth_value(policy_session)?;
710
711    context.policy_command_code(policy_session, CommandCode::Duplicate)?;
712    let digest = context.policy_get_digest(policy_session)?;
713    // end of: create policy digest
714
715    let session = context.start_auth_session(
716        None,
717        None,
718        None,
719        SessionType::Hmac,
720        SymmetricDefinition::AES_256_CFB,
721        HashingAlgorithm::Sha256,
722    )?;
723    let (session_attr, session_mask) = SessionAttributesBuilder::new()
724        .with_decrypt(true)
725        .with_encrypt(true)
726        .build();
727    context.tr_sess_set_attributes(session.unwrap(), session_attr, session_mask)?;
728    context.set_sessions((session, None, None));
729
730    let key_handle = if let (public, Some(private)) = create(spec)? {
731        context.load_external(
732            private.try_into()?,
733            public.with_auth_policy(digest).build()?,
734            Hierarchy::Null,
735        )?
736    } else {
737        panic!("Can import only private keys");
738    };
739
740    let parent_handle =
741        context.load_external_public(create(parent)?.0.build()?, Hierarchy::Null)?;
742
743    let (public, _, _) = context.read_public(key_handle)?;
744
745    let public_key = match &public {
746        Public::Rsa { unique, .. } => PublicKeyBytes::RSA(RsaPublic {
747            bytes: hex::encode(unique.value()),
748        }),
749        Public::Ecc { unique, .. } => PublicKeyBytes::EC(EcPublic {
750            x: hex::encode(unique.x().value()),
751            y: hex::encode(unique.y().value()),
752        }),
753        _ => panic!("Unsupported key type."),
754    };
755
756    let auth_policy = match &public {
757        tss_esapi::structures::Public::Rsa { auth_policy, .. } => auth_policy,
758        tss_esapi::structures::Public::Ecc { auth_policy, .. } => auth_policy,
759        _ => panic!("Unsupported key type."),
760    }
761    .value();
762
763    spec.provider.tpm.unique = Some(public_key);
764    spec.private = None;
765
766    context.set_sessions((None, None, None));
767
768    let policy_auth_session = context
769        .start_auth_session(
770            None,
771            None,
772            None,
773            SessionType::Policy,
774            SymmetricDefinition::AES_256_CFB,
775            HashingAlgorithm::Sha256,
776        )?
777        .expect("Start auth session returned a NONE handle");
778    let (policy_auth_session_attributes, policy_auth_session_attributes_mask) =
779        SessionAttributesBuilder::new()
780            .with_decrypt(true)
781            .with_encrypt(true)
782            .build();
783    context.tr_sess_set_attributes(
784        policy_auth_session,
785        policy_auth_session_attributes,
786        policy_auth_session_attributes_mask,
787    )?;
788
789    let policy_session = PolicySession::try_from(policy_auth_session)?;
790
791    context.policy_auth_value(policy_session)?;
792
793    context.policy_command_code(policy_session, CommandCode::Duplicate)?;
794    context.set_sessions((Some(policy_auth_session), None, None));
795
796    let (data, private, secret) = context.duplicate(
797        key_handle.into(),
798        parent_handle.into(),
799        None,
800        SymmetricDefinitionObject::Null,
801    )?;
802
803    spec.provider.tpm.wrapped = Some(WrappedKey {
804        private: hex::encode(private.value()),
805        secret: hex::encode(secret.value()),
806        data: hex::encode(data.value()),
807    });
808    spec.provider.tpm.policy = Some(hex::encode(auth_policy));
809    spec.provider.tpm.parent = parent.provider.tpm.handle;
810
811    Ok(())
812}