Browse Source

Modified the tpm module so the root key
is the child of a Storage Key.

Matthew Carr 2 years ago
parent
commit
f9cee8eb4e
2 changed files with 163 additions and 67 deletions
  1. 14 7
      crates/btnode/src/crypto/mod.rs
  2. 149 60
      crates/btnode/src/crypto/tpm.rs

+ 14 - 7
crates/btnode/src/crypto/mod.rs

@@ -106,6 +106,20 @@ impl From<TryFromIntError> for Error {
 
 pub(crate) type Result<T> = std::result::Result<T, Error>;
 
+/// Returns an array of the given length filled with cryptographically strong random data.
+fn rand_array<const LEN: usize>() -> Result<[u8; LEN]> {
+    let mut array = [0; LEN];
+    rand_bytes(&mut array)?;
+    Ok(array)
+}
+
+/// Returns a vector of the given length with with cryptographically strong random data.
+fn rand_vec(len: usize) -> Result<Vec<u8>> {
+    let mut vec = vec![0; len];
+    rand_bytes(&mut vec)?;
+    Ok(vec)
+}
+
 /// A cryptographic hash.
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Hashable, Clone, EnumDiscriminants)]
 #[strum_discriminants(derive(EnumString, Display, Serialize, Deserialize))]
@@ -256,13 +270,6 @@ struct SymParams<'a> {
     iv: Option<&'a [u8]>,
 }
 
-/// Returns an array of the given length filled with cryptographically random data.
-fn rand_array<const LEN: usize>() -> Result<[u8; LEN]> {
-    let mut array = [0; LEN];
-    rand_bytes(&mut array)?;
-    Ok(array)
-}
-
 impl SymKey {
     pub(crate) fn generate(kind: SymKeyKind) -> Result<SymKey> {
         match kind {

+ 149 - 60
crates/btnode/src/crypto/tpm.rs

@@ -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() }
     }
 }