|
@@ -10,25 +10,34 @@ use std::{
|
|
|
path::Path,
|
|
|
fs::{File, OpenOptions},
|
|
|
sync::{Arc, Mutex},
|
|
|
+ mem::size_of,
|
|
|
};
|
|
|
use openssl::{
|
|
|
bn::BigNum,
|
|
|
hash::Hasher,
|
|
|
nid::Nid,
|
|
|
};
|
|
|
-use tss_esapi_sys::{TSS2_RC, TPMT_TK_HASHCHECK };
|
|
|
+use tss_esapi_sys::{
|
|
|
+ TSS2_RC,
|
|
|
+ TPMT_TK_HASHCHECK,
|
|
|
+ TPM2_HANDLE,
|
|
|
+ TPM2_MAX_CAP_BUFFER,
|
|
|
+ TPM2_CAP
|
|
|
+};
|
|
|
use tss_esapi::{
|
|
|
Context,
|
|
|
constants::{
|
|
|
session_type::SessionType,
|
|
|
response_code::Tss2ResponseCode,
|
|
|
- tss::TPM2_RH_NULL,
|
|
|
+ tss::{TPM2_RH_NULL, TPM2_PERSISTENT_FIRST},
|
|
|
+ CapabilityType,
|
|
|
},
|
|
|
tcti_ldr::{TctiNameConf, TabrmdConfig},
|
|
|
interface_types::{
|
|
|
resource_handles::Hierarchy,
|
|
|
algorithm::HashingAlgorithm,
|
|
|
key_bits::RsaKeyBits,
|
|
|
+ dynamic_handles::Persistent,
|
|
|
},
|
|
|
structures::{
|
|
|
Digest,
|
|
@@ -45,13 +54,75 @@ use tss_esapi::{
|
|
|
SignatureScheme,
|
|
|
RsaDecryptionScheme,
|
|
|
Data,
|
|
|
+ CapabilityData,
|
|
|
},
|
|
|
attributes::{
|
|
|
object::ObjectAttributes,
|
|
|
},
|
|
|
- handles::KeyHandle,
|
|
|
+ handles::{KeyHandle, PersistentTpmHandle},
|
|
|
};
|
|
|
|
|
|
+impl From<tss_esapi::Error> for Error {
|
|
|
+ fn from(err: tss_esapi::Error) -> Self {
|
|
|
+ match err {
|
|
|
+ tss_esapi::Error::WrapperError(err) => Error::custom(err),
|
|
|
+ tss_esapi::Error::Tss2Error(err) => {
|
|
|
+ let rc = err.tss2_rc();
|
|
|
+ let text = tss2_rc_decode(err);
|
|
|
+ let string = format!("response code: {}, response text: {}", rc, text);
|
|
|
+ Error::custom(string)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<Guard> From<std::sync::PoisonError<Guard>> for Error {
|
|
|
+ fn from(error: std::sync::PoisonError<Guard>) -> Self {
|
|
|
+ Error::custom(error.to_string())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl ContextExt for Context {
|
|
|
+ fn persistent_handles(&mut self) -> Result<Vec<PersistentTpmHandle>> {
|
|
|
+ let capability= CapabilityType::Handles;
|
|
|
+ let property = TPM2_PERSISTENT_FIRST;
|
|
|
+ let max = usize::try_from(TPM2_MAX_CAP_BUFFER).unwrap();
|
|
|
+ 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 (handles, more) = self.get_capability(capability, property, property_count)
|
|
|
+ .unwrap();
|
|
|
+ let list = match handles {
|
|
|
+ CapabilityData::Handles(list) => list.into_inner(),
|
|
|
+ _ => panic!("Unexpected capability type returned by TPM: {:?}", handles),
|
|
|
+ };
|
|
|
+ for handle in list {
|
|
|
+ all_handles.push(PersistentTpmHandle::try_from(handle).conv_err()?);
|
|
|
+ }
|
|
|
+ if !more {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(all_handles)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const COOKIE_LEN: usize = RSA_KEY_BYTES;
|
|
|
|
|
|
struct Cookie([u8; COOKIE_LEN]);
|
|
@@ -76,7 +147,7 @@ impl Cookie {
|
|
|
}
|
|
|
let cookie = Cookie::random()?;
|
|
|
let mut file = OpenOptions::new().write(true).create_new(true).open(&cookie_path)
|
|
|
- .map_err(Error::from)?;
|
|
|
+ .conv_err()?;
|
|
|
cookie.save(&mut file)?;
|
|
|
cookie
|
|
|
}
|
|
@@ -93,18 +164,18 @@ impl Cookie {
|
|
|
}
|
|
|
|
|
|
fn save(&self, to: &mut File) -> Result<()> {
|
|
|
- to.write_all(self.as_slice()).map_err(Error::from)?;
|
|
|
+ to.write_all(self.as_slice()).conv_err()?;
|
|
|
// Only allow read access from the current user.
|
|
|
- let metadata = to.metadata().map_err(Error::from)?;
|
|
|
+ let metadata = to.metadata().conv_err()?;
|
|
|
let mut permissions = metadata.permissions();
|
|
|
permissions.set_mode(0o400);
|
|
|
- to.set_permissions(permissions).map_err(Error::from)?;
|
|
|
+ to.set_permissions(permissions).conv_err()?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
fn load(from: &mut File) -> Result<Cookie> {
|
|
|
let mut cookie = Cookie::empty();
|
|
|
- from.read_exact(cookie.as_mut_slice()).map_err(Error::from)?;
|
|
|
+ from.read_exact(cookie.as_mut_slice()).conv_err()?;
|
|
|
Ok(cookie)
|
|
|
}
|
|
|
}
|
|
@@ -132,8 +203,8 @@ impl TpmCredStore {
|
|
|
SymmetricDefinition::AES_256_CFB,
|
|
|
HashingAlgorithm::Sha256,
|
|
|
)
|
|
|
- .map_err(Error::from)?
|
|
|
- .ok_or_else(|| Error::Message("Received invalid session handle".to_string()))?;
|
|
|
+ .conv_err()?
|
|
|
+ .ok_or_else(|| Error::custom("Received invalid session handle"))?;
|
|
|
context.set_sessions((Some(session), None, None));
|
|
|
let context = Arc::new(Mutex::new(context));
|
|
|
Ok(TpmCredStore { context, cookie })
|
|
@@ -150,18 +221,18 @@ impl TpmCredStore {
|
|
|
.with_sign_encrypt(true)
|
|
|
.with_restricted(false)
|
|
|
.build()
|
|
|
- .map_err(Error::from)?;
|
|
|
+ .conv_err()?;
|
|
|
let name_hashing_algorithm = HashingAlgorithm::Sha256;
|
|
|
let empty = [0u8; 0];
|
|
|
- let auth_policy = Digest::try_from(empty.as_slice()).map_err(Error::from)?;
|
|
|
+ let auth_policy = Digest::try_from(empty.as_slice()).conv_err()?;
|
|
|
let parameters = PublicRsaParameters::new(
|
|
|
SymmetricDefinitionObject::Null,
|
|
|
RsaScheme::Null,
|
|
|
TpmCredStore::RSA_KEY_BITS,
|
|
|
- RsaExponent::try_from(TpmCredStore::RSA_EXPONENT).map_err(Error::from)?,
|
|
|
+ RsaExponent::try_from(TpmCredStore::RSA_EXPONENT).conv_err()?,
|
|
|
);
|
|
|
let unique = PublicKeyRsa::try_from(self.cookie.as_slice())
|
|
|
- .map_err(|e| Error::Message(e.to_string()))?;
|
|
|
+ .conv_err()?;
|
|
|
Public::Rsa {
|
|
|
object_attributes,
|
|
|
name_hashing_algorithm,
|
|
@@ -171,8 +242,8 @@ impl TpmCredStore {
|
|
|
}
|
|
|
};
|
|
|
let result = {
|
|
|
- let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
|
|
|
- lock.create_primary(
|
|
|
+ let mut context = self.context.lock().conv_err()?;
|
|
|
+ context.create_primary(
|
|
|
Hierarchy::Endorsement,
|
|
|
template,
|
|
|
None,
|
|
@@ -180,21 +251,29 @@ impl TpmCredStore {
|
|
|
None,
|
|
|
None,
|
|
|
)
|
|
|
- .map_err(Error::from)?
|
|
|
+ .conv_err()?
|
|
|
};
|
|
|
- let public = match result.out_public {
|
|
|
+ let public = AsymKeyPub::try_from(result.out_public)?;
|
|
|
+
|
|
|
+ Ok(TpmCreds { public, handle: result.key_handle , context: self.context.clone() })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl TryFrom<Public> for AsymKeyPub {
|
|
|
+ type Error = Error;
|
|
|
+
|
|
|
+ fn try_from(public: Public) -> Result<AsymKeyPub> {
|
|
|
+ match public {
|
|
|
Public::Rsa { parameters, unique, .. } => {
|
|
|
let exponent_value = parameters.exponent().value();
|
|
|
- let exponent = BigNum::from_u32(exponent_value).map_err(Error::from)?;
|
|
|
- let modulus = BigNum::from_slice(unique.as_slice()).map_err(Error::from)?;
|
|
|
- let rsa = Rsa::from_public_components(modulus, exponent).map_err(Error::from)?;
|
|
|
- let pkey = PKey::from_rsa(rsa).map_err(Error::from)?;
|
|
|
- AsymKeyPub { pkey, kind: AsymKeyKind::Rsa }
|
|
|
+ let exponent = BigNum::from_u32(exponent_value).conv_err()?;
|
|
|
+ let modulus = BigNum::from_slice(unique.as_slice()).conv_err()?;
|
|
|
+ let rsa = Rsa::from_public_components(modulus, exponent).conv_err()?;
|
|
|
+ let pkey = PKey::from_rsa(rsa).conv_err()?;
|
|
|
+ Ok(AsymKeyPub { pkey, kind: AsymKeyKind::Rsa })
|
|
|
},
|
|
|
- _ => return Err(Error::Message("Unexpected key type returned by TPM".to_string())),
|
|
|
- };
|
|
|
-
|
|
|
- Ok(TpmCreds { public, handle: result.key_handle , context: self.context.clone() })
|
|
|
+ _ => Err(Error::custom("Unsupported key type returned by TPM")),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -247,7 +326,7 @@ impl MessageDigestExt for MessageDigest {
|
|
|
HashingAlgorithm::Sha3_512
|
|
|
}
|
|
|
else {
|
|
|
- return Err(Error::Message(
|
|
|
+ return Err(Error::custom(
|
|
|
format!("Unsupported hash algorithm with NID: {:?}", nid)));
|
|
|
};
|
|
|
Ok(algo)
|
|
@@ -301,29 +380,29 @@ impl Signer for TpmCreds {
|
|
|
fn sign<'a, I: Iterator<Item=&'a [u8]>>(&self, parts: I) -> Result<Signature> {
|
|
|
let msg_digest = self.public.digest();
|
|
|
let digest = {
|
|
|
- let mut hasher = Hasher::new(msg_digest).map_err(Error::from)?;
|
|
|
+ let mut hasher = Hasher::new(msg_digest).conv_err()?;
|
|
|
for part in parts {
|
|
|
- hasher.update(part).map_err(Error::from)?;
|
|
|
+ hasher.update(part).conv_err()?;
|
|
|
}
|
|
|
- let bytes = hasher.finish().map_err(Error::from)?;
|
|
|
+ let bytes = hasher.finish().conv_err()?;
|
|
|
let slice: &[u8] = &bytes;
|
|
|
- Digest::try_from(slice).map_err(|e| Error::Message(e.to_string()))?
|
|
|
+ Digest::try_from(slice).conv_err()?
|
|
|
};
|
|
|
let validation = HashcheckTicket::null();
|
|
|
let scheme = SignatureScheme::RsaSsa { hash_scheme: msg_digest.hash_scheme()? };
|
|
|
let sig = {
|
|
|
- let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
|
|
|
- lock.sign(self.handle, digest, scheme, validation)
|
|
|
- .map_err(|e| Error::Message(e.to_string()))?
|
|
|
+ let mut context = self.context.lock().conv_err()?;
|
|
|
+ context.sign(self.handle, digest, scheme, validation)
|
|
|
+ .conv_err()?
|
|
|
};
|
|
|
let buf = match sig {
|
|
|
tss_esapi::structures::Signature::RsaSsa(inner) => {
|
|
|
let mut buf = [0u8; RSA_KEY_BYTES];
|
|
|
let slice: &[u8] = inner.signature();
|
|
|
- buf.as_mut_slice().write_all(slice).map_err(Error::from)?;
|
|
|
+ buf.as_mut_slice().write_all(slice).conv_err()?;
|
|
|
buf
|
|
|
},
|
|
|
- _ => return Err(Error::Message(format!("Unexpected signature type: {:?}", sig))),
|
|
|
+ _ => return Err(Error::custom(format!("Unexpected signature type: {:?}", sig))),
|
|
|
};
|
|
|
|
|
|
Ok(Signature::Rsa(buf))
|
|
@@ -332,12 +411,12 @@ impl Signer for TpmCreds {
|
|
|
|
|
|
impl Decrypter for TpmCreds {
|
|
|
fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
|
|
|
- let cipher_text = PublicKeyRsa::try_from(slice).map_err(|e| Error::Message(e.to_string()))?;
|
|
|
+ let cipher_text = PublicKeyRsa::try_from(slice).conv_err()?;
|
|
|
let in_scheme = RsaDecryptionScheme::RsaEs;
|
|
|
let empty = [0u8; 0];
|
|
|
- let label = Data::try_from(empty.as_slice()).map_err(|e| Error::Message(e.to_string()))?;
|
|
|
+ let label = Data::try_from(empty.as_slice()).conv_err()?;
|
|
|
let plain_text = {
|
|
|
- let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
|
|
|
+ let mut lock = self.context.lock().conv_err()?;
|
|
|
lock.rsa_decrypt(self.handle, cipher_text, in_scheme, label)?
|
|
|
};
|
|
|
Ok(Vec::from(plain_text.value()))
|
|
@@ -401,26 +480,14 @@ impl HasResponseCode for TSS2_RC {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl From<tss_esapi::Error> for Error {
|
|
|
- fn from(err: tss_esapi::Error) -> Self {
|
|
|
- let string = match err {
|
|
|
- tss_esapi::Error::WrapperError(err) => err.to_string(),
|
|
|
- tss_esapi::Error::Tss2Error(err) => {
|
|
|
- let rc = err.tss2_rc();
|
|
|
- let text = tss2_rc_decode(err);
|
|
|
- format!("response code: {}, response text: {}", rc, text)
|
|
|
- }
|
|
|
- };
|
|
|
- Error::Message(string)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
#[cfg(test)]
|
|
|
mod test {
|
|
|
use super::*;
|
|
|
|
|
|
use tempdir::TempDir;
|
|
|
- use std::fs::File;
|
|
|
+ use std::{
|
|
|
+ fs::File,
|
|
|
+ };
|
|
|
use tss_esapi::{
|
|
|
interface_types::{
|
|
|
ecc::EccCurve,
|
|
@@ -430,18 +497,18 @@ mod test {
|
|
|
EccScheme,
|
|
|
KeyDerivationFunctionScheme,
|
|
|
PublicEccParameters,
|
|
|
- },
|
|
|
+ }
|
|
|
};
|
|
|
use ctor::ctor;
|
|
|
|
|
|
- trait ContextExt {
|
|
|
+ trait TestContextExt {
|
|
|
fn for_test() -> Result<Context>;
|
|
|
}
|
|
|
|
|
|
- impl ContextExt for Context {
|
|
|
+ impl TestContextExt for Context {
|
|
|
fn for_test() -> Result<Context> {
|
|
|
- let config = TabrmdConfig::from_str("bus_type=session").map_err(Error::from)?;
|
|
|
- Context::new(TctiNameConf::Tabrmd(config)).map_err(Error::from)
|
|
|
+ let config = TabrmdConfig::from_str("bus_type=session").conv_err()?;
|
|
|
+ Context::new(TctiNameConf::Tabrmd(config)).conv_err()
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -505,7 +572,7 @@ mod test {
|
|
|
SymmetricDefinition::AES_256_CFB,
|
|
|
HashingAlgorithm::Sha256,
|
|
|
)
|
|
|
- .map_err(Error::from)
|
|
|
+ .conv_err()
|
|
|
.expect("Failed to create session")
|
|
|
.expect("Received invalid handle");
|
|
|
|
|
@@ -518,7 +585,7 @@ mod test {
|
|
|
None,
|
|
|
None,
|
|
|
)
|
|
|
- .map_err(Error::from)
|
|
|
+ .conv_err()
|
|
|
.expect("create_primary failed")
|
|
|
.key_handle;
|
|
|
|
|
@@ -529,11 +596,11 @@ mod test {
|
|
|
/// Tests that a TPM Credential Store can be created when a cookie does not already exist.
|
|
|
#[test]
|
|
|
fn tpm_cred_store_new() -> Result<()> {
|
|
|
- let dir = TempDir::new("btnode").map_err(Error::from)?;
|
|
|
+ let dir = TempDir::new("btnode").conv_err()?;
|
|
|
let cookie_path = dir.path().join("cookie.bin");
|
|
|
let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
|
|
|
- let cookie = File::open(&cookie_path).map_err(Error::from)?;
|
|
|
- let metadata = cookie.metadata().map_err(Error::from)?;
|
|
|
+ let cookie = File::open(&cookie_path).conv_err()?;
|
|
|
+ let metadata = cookie.metadata().conv_err()?;
|
|
|
let actual = metadata.permissions().mode();
|
|
|
// Assert that the cookie can only be read by its owner.
|
|
|
assert_eq!(0o400, 0o777 & actual);
|
|
@@ -544,7 +611,7 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn gen_creds() -> Result<()> {
|
|
|
- let dir = TempDir::new("btnode").map_err(Error::from)?;
|
|
|
+ let dir = TempDir::new("btnode").conv_err()?;
|
|
|
let cookie_path = dir.path().join("cookie.bin");
|
|
|
let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
|
|
|
store.gen_node_creds()?;
|
|
@@ -584,7 +651,7 @@ mod test {
|
|
|
}
|
|
|
|
|
|
fn test_store() -> Result<TpmCredStore> {
|
|
|
- let dir = TempDir::new("btnode").map_err(Error::from)?;
|
|
|
+ let dir = TempDir::new("btnode").conv_err()?;
|
|
|
let cookie_path = dir.path().join("cookie.bin");
|
|
|
let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;
|
|
|
dir.close()?;
|
|
@@ -630,4 +697,12 @@ mod test {
|
|
|
};
|
|
|
assert_eq!(RSA_KEY_BYTES, bytes)
|
|
|
}
|
|
|
+
|
|
|
+ fn list_persistent_handles() {
|
|
|
+ let mut context = Context::for_test().unwrap();
|
|
|
+ let all_handles = context.persistent_handles().unwrap();
|
|
|
+ for handle in all_handles.iter() {
|
|
|
+ println!("{:?}", handle);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|