|
@@ -10,7 +10,7 @@ use std::{
|
|
|
path::{Path, PathBuf},
|
|
|
fs::{OpenOptions},
|
|
|
sync::{Arc, RwLock, RwLockWriteGuard},
|
|
|
- mem::size_of
|
|
|
+ mem::size_of,
|
|
|
};
|
|
|
use openssl::{
|
|
|
bn::BigNum,
|
|
@@ -30,7 +30,7 @@ use tss_esapi::{
|
|
|
session_type::SessionType,
|
|
|
response_code::Tss2ResponseCode,
|
|
|
tss::{TPM2_RH_NULL, TPM2_PERSISTENT_FIRST},
|
|
|
- CapabilityType,
|
|
|
+ CapabilityType, Tss2ResponseCodeKind,
|
|
|
},
|
|
|
tcti_ldr::{TctiNameConf, TabrmdConfig},
|
|
|
interface_types::{
|
|
@@ -88,15 +88,7 @@ trait ContextExt {
|
|
|
fn persistent_handles(&mut self) -> Result<Vec<PersistentTpmHandle>>;
|
|
|
|
|
|
/// Returns the first free persistent handle available in the TPM.
|
|
|
- fn first_free_persistent(&mut self) -> Result<Persistent> {
|
|
|
- let mut handles = self.persistent_handles()?;
|
|
|
- let handle = handles.pop()
|
|
|
- .map_or_else(
|
|
|
- || PersistentTpmHandle::try_from(TPM2_PERSISTENT_FIRST),
|
|
|
- |v| PersistentTpmHandle::new(TPM2_HANDLE::from(v)+ 1))
|
|
|
- .conv_err()?;
|
|
|
- Ok(Persistent::Persistent(handle))
|
|
|
- }
|
|
|
+ fn unused_persistent_primary_key(&mut self) -> Result<Persistent>;
|
|
|
|
|
|
fn persist_key(&mut self, key_handle: KeyHandle) -> Result<TPM2_HANDLE>;
|
|
|
|
|
@@ -112,10 +104,12 @@ impl ContextExt for Context {
|
|
|
let capability = CapabilityType::Handles;
|
|
|
let property = TPM2_PERSISTENT_FIRST;
|
|
|
let max = usize::try_from(TPM2_MAX_CAP_BUFFER).unwrap();
|
|
|
+ // This calculation was copied from tpm2_getcap.
|
|
|
let count = (max - size_of::<TPM2_CAP>() - size_of::<u32>()) / size_of::<TPM2_HANDLE>();
|
|
|
let property_count = u32::try_from(count).unwrap();
|
|
|
let mut all_handles = Vec::new();
|
|
|
- loop {
|
|
|
+ let more = false;
|
|
|
+ for _ in 0..255 {
|
|
|
let (handles, more) = self.get_capability(capability, property, property_count)
|
|
|
.conv_err()?;
|
|
|
let list = match handles {
|
|
@@ -131,17 +125,75 @@ impl ContextExt for Context {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ if more {
|
|
|
+ return Err(Error::custom(
|
|
|
+ "hit iteration limit before retrieving all persistent handles from the TPM"));
|
|
|
+ }
|
|
|
all_handles.sort_unstable_by_key(|handle| TPM2_HANDLE::from(*handle));
|
|
|
Ok(all_handles)
|
|
|
}
|
|
|
|
|
|
+ fn unused_persistent_primary_key(&mut self) -> Result<Persistent> {
|
|
|
+ let mut used_handles = self.persistent_handles()?.into_iter();
|
|
|
+ // These address regions are defined in Registry of Reserved TPM 2.0 Handles and Localities
|
|
|
+ // The first regions for both are for primary keys and the second is marked as "Available".
|
|
|
+ let storage = (0x81_00_00_00..0x81_00_01_00u32).chain(0x81_00_80_00..0x81_01_00_00u32);
|
|
|
+ let endorsement = (0x81_01_00_00..0x81_01_01_00u32).chain(0x81_01_80_00..0x81_02_00_00u32);
|
|
|
+ let possible_handles = storage.chain(endorsement)
|
|
|
+ .map(|handle| PersistentTpmHandle::new(handle).unwrap());
|
|
|
+ let mut unused: Option<PersistentTpmHandle> = None;
|
|
|
+ // We simultaneously iterate over the possible handles and the used handles, breaking when
|
|
|
+ // we find a handle which is not equal to the current used handle, or when
|
|
|
+ // there are no more used handles. This works because the used handles are sorted.
|
|
|
+ for handle in possible_handles {
|
|
|
+ let used = match used_handles.next() {
|
|
|
+ Some(used) => used,
|
|
|
+ None => {
|
|
|
+ unused = Some(handle);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if used != handle {
|
|
|
+ unused = Some(handle);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ match unused {
|
|
|
+ Some(unused) => Ok(Persistent::Persistent(unused)),
|
|
|
+ None => Err(Error::custom("failed to find an unused persistent handle")),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
fn persist_key(&mut self, key_handle: KeyHandle) -> Result<TPM2_HANDLE> {
|
|
|
- let persistent = self.execute_with_session(None, |ctx| ctx.first_free_persistent())?;
|
|
|
- self.evict_control(Provision::Owner, key_handle.into(), persistent).conv_err()?;
|
|
|
- let Persistent::Persistent(inner) = persistent;
|
|
|
- Ok(TPM2_HANDLE::from(inner))
|
|
|
+ let mut persistent: Option<Persistent> = None;
|
|
|
+ for _ in 0..255 {
|
|
|
+ let handle = self.execute_with_session(None, |ctx| {
|
|
|
+ ctx.unused_persistent_primary_key()
|
|
|
+ })?;
|
|
|
+ match self.evict_control(Provision::Owner, key_handle.into(), handle) {
|
|
|
+ Ok(_) => {
|
|
|
+ persistent = Some(handle);
|
|
|
+ break
|
|
|
+ },
|
|
|
+ Err(error) => {
|
|
|
+ if let tss_esapi::Error::Tss2Error(rc) = error {
|
|
|
+ if Some(Tss2ResponseCodeKind::NvDefined) == rc.kind() {
|
|
|
+ // This type of error occurs if another application used the persistent
|
|
|
+ // handle we found before we could, so we try again with a different
|
|
|
+ // handle.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Err(error.into())
|
|
|
+ },
|
|
|
+ }
|
|
|
+ };
|
|
|
+ match persistent {
|
|
|
+ Some(Persistent::Persistent(inner)) => Ok(inner.into()),
|
|
|
+ None => Err(Error::custom("retry limit reached"))
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
fn evict_key(&mut self, persistent: TPM2_HANDLE, key_handle: Option<KeyHandle>) -> Result<()> {
|
|
|
let tpm_handle = TpmHandle::try_from(persistent).conv_err()?;
|
|
|
let key_handle = match key_handle {
|
|
@@ -516,7 +568,7 @@ impl TpmCredStore {
|
|
|
let result = {
|
|
|
let mut guard = self.state.write().conv_err()?;
|
|
|
guard.context.create_primary(
|
|
|
- Hierarchy::Endorsement,
|
|
|
+ Hierarchy::Owner,
|
|
|
params.template()?,
|
|
|
params.auth,
|
|
|
None,
|
|
@@ -1147,7 +1199,7 @@ mod test {
|
|
|
#[test]
|
|
|
fn first_free_persistent() -> Result<()> {
|
|
|
let mut context = Context::for_test()?;
|
|
|
- let _first = context.first_free_persistent()?;
|
|
|
+ let _first = context.unused_persistent_primary_key()?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|