1#![deny(missing_docs)]
2use 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
40pub use tss_esapi::interface_types::algorithm::HashingAlgorithm;
42
43#[derive(Debug, Serialize, Deserialize)]
47pub struct Description {
48 pub spec: Specification,
50}
51
52#[derive(Debug, Serialize, Deserialize)]
58pub struct Specification {
59 pub provider: Provider,
61 pub algo: AlgorithmSpec,
63 pub private: Option<PrivateKeyMaterial>,
65 pub capabilities: Vec<Capability>,
67 pub auth: String,
69}
70
71#[derive(Debug, Serialize, Deserialize)]
75#[serde(rename_all = "lowercase")]
76pub enum PrivateKeyMaterial {
77 Rsa(PrivateRsaKeyMaterial),
79 Ec(EcParameter),
81}
82
83#[derive(Debug, Serialize, Deserialize)]
87pub struct PrivateRsaKeyMaterial {
88 pub prime: String,
90 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#[derive(Debug, Serialize, Deserialize)]
108pub struct EcParameter {
109 pub parameter: String,
111 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(¶m.parameter).unwrap();
118 let mut parameter_buffer = [0u8; 128];
119 parameter_buffer[..parameter.len()].clone_from_slice(¶meter);
120 Self {
121 size: parameter.len() as u16,
122 buffer: parameter_buffer,
123 }
124 }
125}
126
127#[derive(Debug, Serialize, Deserialize, PartialEq)]
138#[serde(rename_all = "lowercase")]
139pub enum Capability {
140 Decrypt,
142 Sign,
144 Restrict,
146 #[serde(other)]
148 Unknown,
149}
150
151#[derive(Debug, Serialize, Deserialize)]
156pub enum AlgorithmSpec {
157 #[serde(rename = "RSA")]
159 Rsa {
160 bits: u16,
162 exponent: Option<u32>,
164 },
165 #[serde(rename = "EC")]
167 Ec {
168 curve: EcCurve,
170 },
171}
172
173#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
177pub enum EcCurve {
178 #[serde(rename = "NIST-P256")]
180 NistP256,
181 #[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#[derive(Debug, Serialize, Deserialize)]
199pub struct Provider {
200 pub tpm: TpmProvider,
202}
203
204#[derive(Debug, Serialize, Deserialize)]
208pub struct TpmProvider {
209 pub tcti: String,
212 pub handle: Option<u32>,
214 pub parent: Option<u32>,
216 pub private: Option<String>,
218 pub unique: Option<PublicKeyBytes>,
220 pub wrapped: Option<WrappedKey>,
222 pub policy: Option<String>,
224}
225
226#[derive(Debug, Serialize, Deserialize)]
231pub struct WrappedKey {
232 pub secret: String,
234 pub private: String,
236 pub data: String,
238}
239
240#[derive(Debug, Serialize, Deserialize)]
245pub enum PublicKeyBytes {
246 RSA(RsaPublic),
248 EC(EcPublic),
250}
251
252#[derive(Debug, Serialize, Deserialize)]
256pub struct RsaPublic {
257 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#[derive(Debug, Serialize, Deserialize)]
272pub struct EcPublic {
273 pub x: String,
275 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
291pub 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
423pub 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
477pub 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
532pub 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
574pub 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
615pub 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
672pub 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 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 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}