From 1ec030368f05d77752ac2a972facf6f9d09b0f0a Mon Sep 17 00:00:00 2001 From: David Mulder Date: Wed, 20 Mar 2024 10:54:03 -0600 Subject: [PATCH] Provide an optional auth value for an IdentityKey Signed-off-by: David Mulder --- src/lib.rs | 59 +++++++++-- src/soft.rs | 14 ++- src/tpm.rs | 283 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 310 insertions(+), 46 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5e7b157..6c73286 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -335,18 +335,34 @@ pub enum LoadableIdentityKey { TpmEcdsa256V1 { private: tpm::Private, public: tpm::Public, + sk_private: Option, + sk_public: Option, x509: Option>, }, #[cfg(not(feature = "tpm"))] - TpmEcdsa256V1 { private: (), public: (), x509: () }, + TpmEcdsa256V1 { + private: (), + public: (), + sk_private: Option<()>, + sk_public: Option<()>, + x509: (), + }, #[cfg(feature = "tpm")] TpmRsa2048V1 { private: tpm::Private, public: tpm::Public, + sk_private: Option, + sk_public: Option, x509: Option>, }, #[cfg(not(feature = "tpm"))] - TpmRsa2048V1 { private: (), public: (), x509: () }, + TpmRsa2048V1 { + private: (), + public: (), + sk_private: Option<()>, + sk_public: Option<()>, + x509: (), + }, } pub enum IdentityKey { @@ -500,12 +516,14 @@ pub trait Tpm { fn identity_key_create( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, algorithm: KeyAlgorithm, ) -> Result; fn identity_key_load( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, ) -> Result; @@ -523,6 +541,7 @@ pub trait Tpm { fn identity_key_certificate_request( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, cn: &str, ) -> Result, TpmError>; @@ -530,6 +549,7 @@ pub trait Tpm { fn identity_key_associate_certificate( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, certificate_der: &[u8], ) -> Result; @@ -638,17 +658,19 @@ impl Tpm for BoxedDynTpm { fn identity_key_create( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, algorithm: KeyAlgorithm, ) -> Result { - self.0.identity_key_create(mk, algorithm) + self.0.identity_key_create(mk, auth_value, algorithm) } fn identity_key_load( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, ) -> Result { - self.0.identity_key_load(mk, loadable_key) + self.0.identity_key_load(mk, auth_value, loadable_key) } fn identity_key_id(&mut self, key: &IdentityKey) -> Result, TpmError> { @@ -671,21 +693,23 @@ impl Tpm for BoxedDynTpm { fn identity_key_certificate_request( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, cn: &str, ) -> Result, TpmError> { self.0 - .identity_key_certificate_request(mk, loadable_key, cn) + .identity_key_certificate_request(mk, auth_value, loadable_key, cn) } fn identity_key_associate_certificate( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, certificate_der: &[u8], ) -> Result { self.0 - .identity_key_associate_certificate(mk, loadable_key, certificate_der) + .identity_key_associate_certificate(mk, auth_value, loadable_key, certificate_der) } fn identity_key_public_as_der(&mut self, key: &IdentityKey) -> Result, TpmError> { @@ -885,15 +909,20 @@ mod tests { .machine_key_load(&auth_value, &loadable_machine_key) .expect("Unable to load machine key"); + let id_key_auth_str = AuthValue::generate().expect("Failed to create hex pin"); + + let id_key_auth_value = + AuthValue::from_str(&id_key_auth_str).expect("Unable to create auth value"); + // from that ctx, create an identity key let loadable_id_key = $tpm - .identity_key_create(&machine_key, $alg) + .identity_key_create(&machine_key, Some(&id_key_auth_value), $alg) .expect("Unable to create id key"); trace!(?loadable_id_key); let id_key = $tpm - .identity_key_load(&machine_key, &loadable_id_key) + .identity_key_load(&machine_key, Some(&id_key_auth_value), &loadable_id_key) .expect("Unable to load id key"); let id_key_public_pem = $tpm @@ -965,9 +994,11 @@ mod tests { .machine_key_load(&auth_value, &loadable_machine_key) .expect("Unable to load machine key"); + let id_key_auth_value = AuthValue::ephemeral().expect("Unable to create auth value"); + // from that ctx, create an identity key let loadable_id_key = $tpm - .identity_key_create(&machine_key, $alg) + .identity_key_create(&machine_key, Some(&id_key_auth_value), $alg) .expect("Unable to create id key"); trace!(?loadable_id_key); @@ -975,7 +1006,12 @@ mod tests { // Get the CSR let csr_der = $tpm - .identity_key_certificate_request(&machine_key, &loadable_id_key, "common name") + .identity_key_certificate_request( + &machine_key, + Some(&id_key_auth_value), + &loadable_id_key, + "common name", + ) .expect("Failed to create csr"); // Now, we need to sign this to an x509 cert externally. @@ -992,6 +1028,7 @@ mod tests { let loadable_id_key = $tpm .identity_key_associate_certificate( &machine_key, + Some(&id_key_auth_value), &loadable_id_key, &signed_cert_der, ) @@ -999,7 +1036,7 @@ mod tests { // Now load it in: let id_key = $tpm - .identity_key_load(&machine_key, &loadable_id_key) + .identity_key_load(&machine_key, Some(&id_key_auth_value), &loadable_id_key) .expect("Unable to load id key"); let id_key_x509_pem = $tpm diff --git a/src/soft.rs b/src/soft.rs index c058b58..c274a0e 100644 --- a/src/soft.rs +++ b/src/soft.rs @@ -147,8 +147,12 @@ impl Tpm for SoftTpm { fn identity_key_create( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, algorithm: KeyAlgorithm, ) -> Result { + if auth_value.is_some() { + return Err(TpmError::TpmOperationUnsupported); + } match algorithm { KeyAlgorithm::Ecdsa256 => { let ecgroup = @@ -224,8 +228,12 @@ impl Tpm for SoftTpm { fn identity_key_load( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, ) -> Result { + if auth_value.is_some() { + return Err(TpmError::TpmOperationUnsupported); + } match (mk, loadable_key) { ( MachineKey::SoftAes256Gcm { key: mk_key }, @@ -457,10 +465,11 @@ impl Tpm for SoftTpm { fn identity_key_certificate_request( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, cn: &str, ) -> Result, TpmError> { - let id_key = self.identity_key_load(mk, loadable_key)?; + let id_key = self.identity_key_load(mk, auth_value, loadable_key)?; let mut req_builder = X509ReqBuilder::new().map_err(|ossl_err| { error!(?ossl_err); @@ -516,10 +525,11 @@ impl Tpm for SoftTpm { fn identity_key_associate_certificate( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, certificate_der: &[u8], ) -> Result { - let id_key = self.identity_key_load(mk, loadable_key)?; + let id_key = self.identity_key_load(mk, auth_value, loadable_key)?; // Verify the certificate matches our key let certificate = X509::from_der(certificate_der).map_err(|ossl_err| { diff --git a/src/tpm.rs b/src/tpm.rs index 14678cd..851e2d3 100644 --- a/src/tpm.rs +++ b/src/tpm.rs @@ -648,6 +648,7 @@ impl Tpm for TpmTss { fn identity_key_create( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, algorithm: KeyAlgorithm, ) -> Result { let mk_key_context = match mk { @@ -718,62 +719,173 @@ impl Tpm for TpmTss { } }; - self.execute_with_temporary_object_context(mk_key_context, |hsm_ctx, mk_key_handle| { - hsm_ctx - .tpm_ctx - .create(mk_key_handle.into(), key_pub, None, None, None, None) - .map( - |CreateKeyResult { - out_private: private, - out_public: public, - creation_data: _, - creation_hash: _, - creation_ticket: _, - }| { - match algorithm { - KeyAlgorithm::Ecdsa256 => LoadableIdentityKey::TpmEcdsa256V1 { - private, - public, - x509: None, - }, - KeyAlgorithm::Rsa2048 => LoadableIdentityKey::TpmRsa2048V1 { - private, - public, - x509: None, - }, - } - }, - ) - .map_err(|tpm_err| { - error!(?tpm_err); - TpmError::TpmIdentityKeyCreate - }) - }) + if let Some(auth_value) = auth_value { + let tpm_auth_value = match auth_value { + AuthValue::Key256Bit { auth_key } => Auth::from_bytes(auth_key.as_ref()), + } + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmAuthValueInvalid + })?; + + self.execute_with_temporary_object_context( + mk_key_context, + |hsm_ctx, primary_key_handle| { + let (private, public) = hsm_ctx + .tpm_ctx + .create( + primary_key_handle.into(), + key_pub, + Some(tpm_auth_value.clone()), + None, + None, + None, + ) + .map( + |CreateKeyResult { + out_private: private, + out_public: public, + creation_data: _, + creation_hash: _, + creation_ticket: _, + }| { (private, public) }, + ) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmIdentityKeyCreate + })?; + + let storage_key_pub = Self::create_storage_key_public()?; + + // Now do a temporary load and create for the storage key. + let key_handle = hsm_ctx + .tpm_ctx + .load(primary_key_handle.into(), private.clone(), public.clone()) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + })?; + + // Now it's loaded, create the machine storage key + let (sk_private, sk_public) = hsm_ctx.execute_with_temporary_object( + key_handle.into(), + |hsm_ctx, mk_key_handle| { + hsm_ctx + .tpm_ctx + .tr_set_auth(mk_key_handle, tpm_auth_value.clone()) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + })?; + + hsm_ctx + .tpm_ctx + .create(key_handle, storage_key_pub, None, None, None, None) + .map( + |CreateKeyResult { + out_private: private, + out_public: public, + creation_data: _, + creation_hash: _, + creation_ticket: _, + }| { (private, public) }, + ) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmIdentityKeyCreate + }) + }, + )?; + + Ok(match algorithm { + KeyAlgorithm::Ecdsa256 => LoadableIdentityKey::TpmEcdsa256V1 { + private, + public, + sk_private: Some(sk_private), + sk_public: Some(sk_public), + x509: None, + }, + KeyAlgorithm::Rsa2048 => LoadableIdentityKey::TpmRsa2048V1 { + private, + public, + sk_private: Some(sk_private), + sk_public: Some(sk_public), + x509: None, + }, + }) + }, + ) + } else { + self.execute_with_temporary_object_context(mk_key_context, |hsm_ctx, mk_key_handle| { + hsm_ctx + .tpm_ctx + .create(mk_key_handle.into(), key_pub, None, None, None, None) + .map( + |CreateKeyResult { + out_private: private, + out_public: public, + creation_data: _, + creation_hash: _, + creation_ticket: _, + }| { + match algorithm { + KeyAlgorithm::Ecdsa256 => LoadableIdentityKey::TpmEcdsa256V1 { + private, + public, + sk_private: None, + sk_public: None, + x509: None, + }, + KeyAlgorithm::Rsa2048 => LoadableIdentityKey::TpmRsa2048V1 { + private, + public, + sk_private: None, + sk_public: None, + x509: None, + }, + } + }, + ) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmIdentityKeyCreate + }) + }) + } } fn identity_key_load( &mut self, mk: &MachineKey, + auth_value: Option<&AuthValue>, loadable_key: &LoadableIdentityKey, ) -> Result { - let (private, public, algorithm, x509) = match loadable_key { + let (private, public, sk_private, sk_public, algorithm, x509) = match loadable_key { LoadableIdentityKey::TpmEcdsa256V1 { private, public, + sk_private, + sk_public, x509, } => ( private.clone(), public.clone(), + sk_private.clone(), + sk_public.clone(), KeyAlgorithm::Ecdsa256, x509.as_ref(), ), LoadableIdentityKey::TpmRsa2048V1 { private, public, + sk_private, + sk_public, x509, } => ( private.clone(), public.clone(), + sk_private.clone(), + sk_public.clone(), KeyAlgorithm::Rsa2048, x509.as_ref(), ), @@ -808,11 +920,114 @@ impl Tpm for TpmTss { _ => return Err(TpmError::IncorrectKeyType), }; - self.execute_key_load_to_context(mk_key_context, private, public) + if let Some(auth_value) = auth_value { + let auth_value = match auth_value { + AuthValue::Key256Bit { auth_key } => Auth::from_bytes(auth_key.as_ref()), + } + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmAuthValueInvalid + })?; + let sk_private = sk_private.ok_or({ + error!("Key was not created with an auth value"); + TpmError::TpmKeyLoad + })?; + let sk_public = sk_public.ok_or({ + error!("Key was not created with an auth value"); + TpmError::TpmKeyLoad + })?; + + // Load the root storage key. This is what has the authValue attached, which we + // need to supply to use it. + let root_key_handle = self.execute_with_temporary_object_context( + mk_key_context, + |hsm_ctx, primary_key_handle| { + hsm_ctx + .tpm_ctx + .load(primary_key_handle.into(), private.clone(), public.clone()) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + }) + }, + )?; + + // At the end of this fn, root_key_handle is unloaded and our storage key + // handle is ready to rock. + let key_handle = self.execute_with_temporary_object( + root_key_handle.into(), + |hsm_ctx, root_key_handle| { + hsm_ctx + .tpm_ctx + .tr_set_auth(root_key_handle, auth_value.clone()) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + })?; + + hsm_ctx + .tpm_ctx + .load( + root_key_handle.into(), + sk_private.clone(), + sk_public.clone(), + ) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + }) + }, + )?; + + // Load the subordinate storage key. This is what roots our actual storage because + // when you unload/load a context with an authValue you must always supply that + // authValue. To reduce the need to keep authValue in memory and ship it around, we + // have a subordinate key without an authValue that can only exist if the parent + // with the authValue was supplied at least once. + + self.execute_with_temporary_object(key_handle.into(), |hsm_ctx, key_handle| { + hsm_ctx + .tpm_ctx + .context_save(key_handle) + .map(|key_context| match algorithm { + KeyAlgorithm::Ecdsa256 => IdentityKey::TpmEcdsa256 { key_context, x509 }, + KeyAlgorithm::Rsa2048 => IdentityKey::TpmRsa2048 { key_context, x509 }, + }) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + }) + }) + } else { + // Load our private/public under our parent context, and immediately + // flush the parent handle from the context. + let key_handle = self.execute_with_temporary_object_context( + mk_key_context, + |hsm_ctx, parent_key_handle| { + hsm_ctx + .tpm_ctx + .load(parent_key_handle.into(), private, public) + .map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmKeyLoad + }) + }, + )?; + + // The object is now loaded, and the parent key has been released. Now we can + // save the context for the child key so that we don't fill up objectMemory in + // the context. + self.execute_with_temporary_object(key_handle.into(), |hsm_ctx, key_handle| { + hsm_ctx.tpm_ctx.context_save(key_handle).map_err(|tpm_err| { + error!(?tpm_err); + TpmError::TpmContextSave + }) + }) .map(|key_context| match algorithm { KeyAlgorithm::Ecdsa256 => IdentityKey::TpmEcdsa256 { key_context, x509 }, KeyAlgorithm::Rsa2048 => IdentityKey::TpmRsa2048 { key_context, x509 }, }) + } } fn identity_key_id(&mut self, key: &IdentityKey) -> Result, TpmError> { @@ -1020,6 +1235,7 @@ impl Tpm for TpmTss { fn identity_key_certificate_request( &mut self, _mk: &MachineKey, + _auth_value: Option<&AuthValue>, _loadable_key: &LoadableIdentityKey, _cn: &str, ) -> Result, TpmError> { @@ -1029,6 +1245,7 @@ impl Tpm for TpmTss { fn identity_key_associate_certificate( &mut self, _mk: &MachineKey, + _auth_value: Option<&AuthValue>, _loadable_key: &LoadableIdentityKey, _certificate_der: &[u8], ) -> Result {