|
@@ -83,6 +83,7 @@ impl<Guard> From<std::sync::PoisonError<Guard>> for Error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Trait which extends the `tss_esapi::Context`.
|
|
|
trait ContextExt {
|
|
|
fn persistent_handles(&mut self) -> Result<Vec<PersistentTpmHandle>>;
|
|
|
|
|
@@ -247,6 +248,7 @@ impl DigestExt for Digest {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// An authentication cookie which grants access to a node's credentials.
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
|
struct Cookie(
|
|
|
#[serde(with = "BigArray")]
|
|
@@ -279,6 +281,7 @@ impl Cookie {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// A public/private key pair in a form which can be serialized and deserialized.
|
|
|
#[derive(Serialize, Clone)]
|
|
|
struct StoredKeyPair<S: Scheme> {
|
|
|
public: AsymKeyPub<S>,
|
|
@@ -314,29 +317,27 @@ impl<'de, S: Scheme> Deserialize<'de> for StoredKeyPair<S> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Credentials in a form which can be serialized and deserialized.
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
|
-struct TpmHandles {
|
|
|
+struct StoredCredData {
|
|
|
sign: StoredKeyPair<Sign>,
|
|
|
enc: StoredKeyPair<Encrypt>,
|
|
|
}
|
|
|
|
|
|
-impl TpmHandles {
|
|
|
- fn new(sign: StoredKeyPair<Sign>, enc: StoredKeyPair<Encrypt>) -> TpmHandles {
|
|
|
- TpmHandles { sign, enc }
|
|
|
- }
|
|
|
-
|
|
|
- fn to_key_handles(&self, context: &mut Context) -> Result<KeyHandles> {
|
|
|
+impl StoredCredData {
|
|
|
+ fn to_cred_data(&self, context: &mut Context) -> Result<CredData> {
|
|
|
let sign = KeyPair::from_stored(context, &self.sign)?;
|
|
|
let enc = KeyPair::from_stored(context, &self.enc)?;
|
|
|
- Ok(KeyHandles {sign, enc})
|
|
|
- }
|
|
|
+ Ok(CredData {sign, enc })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
struct Storage {
|
|
|
cookie: Cookie,
|
|
|
- node: Option<TpmHandles>,
|
|
|
- root: Option<TpmHandles>,
|
|
|
+ node: Option<StoredCredData>,
|
|
|
+ root: Option<StoredCredData>,
|
|
|
+ storage_key: Option<StoredKeyPair<Encrypt>>,
|
|
|
}
|
|
|
|
|
|
impl Storage {
|
|
@@ -345,6 +346,7 @@ impl Storage {
|
|
|
cookie,
|
|
|
node: None,
|
|
|
root: None,
|
|
|
+ storage_key: None,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -425,6 +427,11 @@ impl TryFrom<KeyLen> for RsaKeyBits {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+enum Parent {
|
|
|
+ Seed(Hierarchy),
|
|
|
+ Key(KeyHandle),
|
|
|
+}
|
|
|
+
|
|
|
struct KeyBuilder<'a, S: Scheme> {
|
|
|
scheme: S,
|
|
|
allow_dup: bool,
|
|
@@ -432,6 +439,9 @@ struct KeyBuilder<'a, S: Scheme> {
|
|
|
auth_value: Option<Auth>,
|
|
|
name_hash: HashingAlgorithm,
|
|
|
policy_digest: Digest,
|
|
|
+ parent: Parent,
|
|
|
+ restricted: bool,
|
|
|
+ symmetric: SymmetricDefinitionObject,
|
|
|
}
|
|
|
|
|
|
impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
@@ -443,6 +453,9 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
auth_value: None,
|
|
|
name_hash: HashingAlgorithm::Sha256,
|
|
|
policy_digest: Digest::empty(),
|
|
|
+ parent: Parent::Seed(Hierarchy::Owner),
|
|
|
+ restricted: false,
|
|
|
+ symmetric: SymmetricDefinitionObject::Null,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -466,6 +479,21 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
self
|
|
|
}
|
|
|
|
|
|
+ fn with_parent(mut self, parent: Parent) -> Self {
|
|
|
+ self.parent = parent;
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ fn with_restricted(mut self, restricted: bool) -> Self {
|
|
|
+ self.restricted = restricted;
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ fn with_symmetric(mut self, symmetric: SymmetricDefinitionObject) -> Self {
|
|
|
+ self.symmetric = symmetric;
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
fn rsa_template(&self) -> Result<Public> {
|
|
|
let scheme_enum = self.scheme.as_enum();
|
|
|
let decrypt = match scheme_enum {
|
|
@@ -479,12 +507,12 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
.with_user_with_auth(true)
|
|
|
.with_decrypt(decrypt)
|
|
|
.with_sign_encrypt(!decrypt)
|
|
|
- .with_restricted(false)
|
|
|
+ .with_restricted(self.restricted)
|
|
|
.build()?;
|
|
|
let name_hashing_algorithm = self.name_hash;
|
|
|
let parameters = PublicRsaParameters::new(
|
|
|
- SymmetricDefinitionObject::Null,
|
|
|
- scheme_enum.try_into()?,
|
|
|
+ self.symmetric,
|
|
|
+ if self.restricted { RsaScheme::Null } else { scheme_enum.try_into()? },
|
|
|
self.scheme.key_len().try_into()?,
|
|
|
Rsa::EXP.try_into()?,
|
|
|
);
|
|
@@ -509,8 +537,43 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ fn build(self, context: &mut Context) -> Result<KeyPair<S>> {
|
|
|
+ let (public, private) = match self.parent {
|
|
|
+ Parent::Seed(hierarchy) => {
|
|
|
+ let result = context.create_primary(
|
|
|
+ hierarchy,
|
|
|
+ self.template()?,
|
|
|
+ self.auth_value,
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ )?;
|
|
|
+ (result.out_public, result.key_handle)
|
|
|
+ }
|
|
|
+ Parent::Key(parent) => {
|
|
|
+ let result = context.create(
|
|
|
+ parent,
|
|
|
+ self.template()?,
|
|
|
+ self.auth_value,
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ )?;
|
|
|
+ let private = context.load(
|
|
|
+ parent,
|
|
|
+ result.out_private,
|
|
|
+ result.out_public.clone(),
|
|
|
+ )?;
|
|
|
+ (result.out_public, private)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ let public = AsymKey::try_from(public, self.scheme)?;
|
|
|
+ Ok(KeyPair { public, private })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+/// A public/private key pair.
|
|
|
#[derive(Clone)]
|
|
|
struct KeyPair<S: Scheme> {
|
|
|
public: AsymKeyPub<S>,
|
|
@@ -531,7 +594,8 @@ impl<S: Scheme> KeyPair<S> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-struct KeyHandles {
|
|
|
+/// Credentials which can be used for signing and encrypting.
|
|
|
+struct CredData {
|
|
|
sign: KeyPair<Sign>,
|
|
|
enc: KeyPair<Encrypt>,
|
|
|
}
|
|
@@ -540,11 +604,16 @@ struct State {
|
|
|
context: Context,
|
|
|
storage: Storage,
|
|
|
node_creds: Option<TpmCreds>,
|
|
|
+ storage_key: Option<KeyPair<Encrypt>>,
|
|
|
}
|
|
|
|
|
|
impl State {
|
|
|
- fn new(context: Context, storage: Storage) -> State {
|
|
|
- State { context, storage, node_creds: None }
|
|
|
+ fn new(mut context: Context, storage: Storage) -> Result<State> {
|
|
|
+ let storage_key = match &storage.storage_key {
|
|
|
+ Some(storage_key) => Some(KeyPair::from_stored(&mut context, storage_key)?),
|
|
|
+ None => None,
|
|
|
+ };
|
|
|
+ Ok(State { context, storage, node_creds: None, storage_key, })
|
|
|
}
|
|
|
|
|
|
fn init_node_creds(&mut self, state: &Arc<RwLock<State>>) -> Result<()> {
|
|
@@ -552,7 +621,7 @@ impl State {
|
|
|
Some(handles) => handles,
|
|
|
None => return Ok(()),
|
|
|
};
|
|
|
- let key_handles = tpm_handles.to_key_handles(&mut self.context)?;
|
|
|
+ let key_handles = tpm_handles.to_cred_data(&mut self.context)?;
|
|
|
let auth = self.storage.cookie.auth();
|
|
|
self.context.tr_set_auth(key_handles.enc.private.into(), auth.clone())?;
|
|
|
self.context.tr_set_auth(key_handles.sign.private.into(), auth)?;
|
|
@@ -576,7 +645,7 @@ impl TpmCredStore {
|
|
|
let session = context.start_default_auth_session()?;
|
|
|
context.set_sessions((Some(session), None, None));
|
|
|
let cookie = storage.cookie.clone();
|
|
|
- let state = Arc::new(RwLock::new(State::new(context, storage)));
|
|
|
+ let state = Arc::new(RwLock::new(State::new(context, storage)?));
|
|
|
{
|
|
|
let mut guard = state.write()?;
|
|
|
guard.init_node_creds(&state)?;
|
|
@@ -592,37 +661,7 @@ impl TpmCredStore {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- fn gen_key<S: Scheme>(&self, params: KeyBuilder<S>) -> Result<KeyPair<S>> {
|
|
|
- let result = {
|
|
|
- let mut guard = self.state.write()?;
|
|
|
- guard.context.create_primary(
|
|
|
- Hierarchy::Owner,
|
|
|
- params.template()?,
|
|
|
- params.auth_value,
|
|
|
- None,
|
|
|
- None,
|
|
|
- None,
|
|
|
- )?
|
|
|
- };
|
|
|
- let public = AsymKey::try_from(result.out_public, params.scheme)?;
|
|
|
- Ok(KeyPair { public, private: result.key_handle })
|
|
|
- }
|
|
|
-
|
|
|
- fn gen_node_sign_key(&self) -> Result<KeyPair<Sign>> {
|
|
|
- let params = KeyBuilder::new(Self::SIGN_SCHEME, self.cookie.as_slice())
|
|
|
- .with_allow_dup(false)
|
|
|
- .with_auth(self.cookie.auth());
|
|
|
- self.gen_key(params)
|
|
|
- }
|
|
|
-
|
|
|
- fn gen_node_enc_key(&self) -> Result<KeyPair<Encrypt>> {
|
|
|
- let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
|
|
|
- .with_allow_dup(false)
|
|
|
- .with_auth(self.cookie.auth());
|
|
|
- self.gen_key(params)
|
|
|
- }
|
|
|
-
|
|
|
- fn persist<F: FnOnce(&mut Storage, TpmHandles)>(
|
|
|
+ fn persist<F: FnOnce(&mut Storage, StoredCredData)>(
|
|
|
&self, creds: &TpmCreds, update_storage: F
|
|
|
) -> Result<()> {
|
|
|
let mut guard = self.state.write()?;
|
|
@@ -634,10 +673,10 @@ impl TpmCredStore {
|
|
|
return Err(error);
|
|
|
}
|
|
|
};
|
|
|
- let handles = TpmHandles::new(
|
|
|
- creds.sign.to_stored(sign_handle),
|
|
|
- creds.enc.to_stored(enc_handle)
|
|
|
- );
|
|
|
+ let handles = StoredCredData {
|
|
|
+ sign: creds.sign.to_stored(sign_handle),
|
|
|
+ enc: creds.enc.to_stored(enc_handle),
|
|
|
+ };
|
|
|
update_storage(&mut guard.storage, handles);
|
|
|
match self.save_storage(&mut guard) {
|
|
|
Ok(_) => Ok(()),
|
|
@@ -655,6 +694,52 @@ impl TpmCredStore {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ fn get_or_init_storage_key(&self) -> Result<KeyPair<Encrypt>> {
|
|
|
+ {
|
|
|
+ let guard = self.state.read()?;
|
|
|
+ if let Some(storage_key) = &guard.storage_key {
|
|
|
+ return Ok(storage_key.clone());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
|
|
|
+ .with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
+ .with_allow_dup(false)
|
|
|
+ .with_auth(self.cookie.auth())
|
|
|
+ .with_restricted(true)
|
|
|
+ .with_symmetric(SymmetricDefinitionObject::AES_256_CFB);
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ let storage_key = params.build(&mut guard.context)?;
|
|
|
+ guard.storage_key = Some(storage_key.clone());
|
|
|
+ let tpm_handle = guard.context.persist_key(storage_key.private)?;
|
|
|
+ guard.storage.storage_key = Some(storage_key.to_stored(tpm_handle));
|
|
|
+ if let Err(err) = self.save_storage(&mut guard) {
|
|
|
+ guard.context.evict_key(tpm_handle, Some(storage_key.private))?;
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ Ok(storage_key)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn gen_key<S: Scheme>(&self, params: KeyBuilder<S>) -> Result<KeyPair<S>> {
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ params.build(&mut guard.context)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn gen_node_sign_key(&self) -> Result<KeyPair<Sign>> {
|
|
|
+ let params = KeyBuilder::new(Self::SIGN_SCHEME, self.cookie.as_slice())
|
|
|
+ .with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
+ .with_allow_dup(false)
|
|
|
+ .with_auth(self.cookie.auth());
|
|
|
+ self.gen_key(params)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn gen_node_enc_key(&self) -> Result<KeyPair<Encrypt>> {
|
|
|
+ let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
|
|
|
+ .with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
+ .with_allow_dup(false)
|
|
|
+ .with_auth(self.cookie.auth());
|
|
|
+ self.gen_key(params)
|
|
|
+ }
|
|
|
+
|
|
|
fn gen_node_creds(&self) -> Result<TpmCreds> {
|
|
|
let sign = self.gen_node_sign_key()?;
|
|
|
let enc = self.gen_node_enc_key()?;
|
|
@@ -664,18 +749,22 @@ impl TpmCredStore {
|
|
|
}
|
|
|
|
|
|
fn gen_root_sign_key(&self, password: &str) -> Result<KeyPair<Sign>> {
|
|
|
- let unique: [u8; Cookie::LEN] = rand_array()?;
|
|
|
+ let unique = rand_vec(Self::ENCRYPT_SCHEME.key_len() as usize)?;
|
|
|
+ let storage_key = self.get_or_init_storage_key()?;
|
|
|
let params = KeyBuilder::new(Self::SIGN_SCHEME, unique.as_slice())
|
|
|
+ .with_parent(Parent::Key(storage_key.private))
|
|
|
.with_allow_dup(true)
|
|
|
- .with_auth(Auth::try_from(password.as_bytes())?);
|
|
|
+ .with_auth(password.as_bytes().try_into()?);
|
|
|
self.gen_key(params)
|
|
|
}
|
|
|
|
|
|
fn gen_root_enc_key(&self, password: &str) -> Result<KeyPair<Encrypt>> {
|
|
|
- let unique: [u8; Cookie::LEN] = rand_array()?;
|
|
|
+ let unique = rand_vec(Self::ENCRYPT_SCHEME.key_len() as usize)?;
|
|
|
+ let storage_key = self.get_or_init_storage_key()?;
|
|
|
let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, unique.as_slice())
|
|
|
+ .with_parent(Parent::Key(storage_key.private))
|
|
|
.with_allow_dup(true)
|
|
|
- .with_auth(Auth::try_from(password.as_bytes())?);
|
|
|
+ .with_auth(password.as_bytes().try_into()?);
|
|
|
self.gen_key(params)
|
|
|
}
|
|
|
}
|
|
@@ -701,7 +790,7 @@ impl CredStore for TpmCredStore {
|
|
|
.clone()
|
|
|
};
|
|
|
let mut guard = self.state.write()?;
|
|
|
- let key_handles = root_handles.to_key_handles(&mut guard.context)?;
|
|
|
+ let key_handles = root_handles.to_cred_data(&mut guard.context)?;
|
|
|
let auth = Auth::try_from(password.as_bytes())?;
|
|
|
guard.context.tr_set_auth(key_handles.sign.private.into(), auth.clone())?;
|
|
|
guard.context.tr_set_auth(key_handles.enc.private.into(), auth)?;
|
|
@@ -819,7 +908,7 @@ pub(crate) struct TpmCreds {
|
|
|
}
|
|
|
|
|
|
impl TpmCreds {
|
|
|
- fn new(key_handles: KeyHandles, state: &Arc<RwLock<State>>) -> TpmCreds {
|
|
|
+ fn new(key_handles: CredData, state: &Arc<RwLock<State>>) -> TpmCreds {
|
|
|
TpmCreds { sign: key_handles.sign, enc: key_handles.enc, state: state.clone() }
|
|
|
}
|
|
|
}
|