Browse Source

Implemented decryption for TPM resident creds.

Matthew Carr 2 years ago
parent
commit
304379b025

+ 11 - 0
crates/btnode/Cargo.lock

@@ -60,6 +60,7 @@ name = "btnode"
 version = "0.1.0"
 dependencies = [
  "base64-url",
+ "ctor",
  "env_logger",
  "harness",
  "log",
@@ -86,6 +87,16 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "ctor"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb"
+dependencies = [
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "enumflags2"
 version = "0.7.5"

+ 2 - 1
crates/btnode/Cargo.toml

@@ -21,4 +21,5 @@ tss-esapi = "7.1.0"
 tss-esapi-sys = "0.3.0"
 
 [dev-dependencies]
-tempdir = "0.3.7"
+tempdir = "0.3.7"
+ctor = "0.1.22"

+ 6 - 6
crates/btnode/src/crypto/mod.rs

@@ -161,20 +161,20 @@ impl Display for Hash {
     }
 }
 
-pub(crate) const RSA_SIG_LEN: usize = 384;
+pub(crate) const RSA_KEY_BYTES: usize = 384;
 
 /// A cryptographic signature.
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone, EnumDiscriminants)]
 #[strum_discriminants(name(SignatureKind))]
 pub(crate) enum Signature {
     #[serde(with = "BigArray")]
-    Rsa([u8; RSA_SIG_LEN]),
+    Rsa([u8; RSA_KEY_BYTES]),
 }
 
 impl Signature {
     fn new(id: SignatureKind) -> Signature {
         match id {
-            SignatureKind::Rsa => Signature::Rsa([0; RSA_SIG_LEN])
+            SignatureKind::Rsa => Signature::Rsa([0; RSA_KEY_BYTES])
         }
     }
 
@@ -205,7 +205,7 @@ impl AsMut<[u8]> for Signature {
 
 impl Default for Signature {
     fn default() -> Self {
-        Signature::Rsa([0; RSA_SIG_LEN])
+        Signature::Rsa([0; RSA_KEY_BYTES])
     }
 }
 
@@ -435,7 +435,7 @@ impl<T: CredsPriv> ConcreteCreds<T> {
 
 impl ConcreteCreds<RsaPriv> {
     pub(crate) fn generate() -> Result<ConcreteCreds<RsaPriv>> {
-        let key_bits = 8 * u32::try_from(RSA_SIG_LEN).map_err(|e| Error::Message(e.to_string()))?;
+        let key_bits = 8 * u32::try_from(RSA_KEY_BYTES).map_err(|e| Error::Message(e.to_string()))?;
         let key = Rsa::generate(key_bits)?;
         // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
         let public_der = key.public_key_to_der().map_err(Error::from)?;
@@ -966,7 +966,7 @@ mod tests {
             let length = match signature {
                 Signature::Rsa(data) => data.len(),
             };
-            assert_eq!(RSA_SIG_LEN, length);
+            assert_eq!(RSA_KEY_BYTES, length);
             Ok(())
         }
     }

+ 65 - 13
crates/btnode/src/crypto/tpm.rs

@@ -6,11 +6,16 @@ use std::{
         raw::c_char,
         unix::fs::PermissionsExt,
     },
-    ffi::CStr, path::{Path},
+    ffi::CStr,
+    path::Path,
     fs::{File, OpenOptions},
     sync::{Arc, Mutex},
 };
-use openssl::{bn::BigNum, hash::Hasher, nid::Nid};
+use openssl::{
+    bn::BigNum,
+    hash::Hasher,
+    nid::Nid,
+};
 use tss_esapi_sys::{TSS2_RC, TPMT_TK_HASHCHECK };
 use tss_esapi::{
     Context,
@@ -38,6 +43,8 @@ use tss_esapi::{
         HashcheckTicket,
         Ticket,
         SignatureScheme,
+        RsaDecryptionScheme,
+        Data,
     },
     attributes::{
         object::ObjectAttributes,
@@ -45,7 +52,7 @@ use tss_esapi::{
     handles::KeyHandle,
 };
 
-const COOKIE_LEN: usize = RSA_SIG_LEN;
+const COOKIE_LEN: usize = RSA_KEY_BYTES;
 
 struct Cookie([u8; COOKIE_LEN]);
 
@@ -108,6 +115,11 @@ pub(crate) struct TpmCredStore {
 }
 
 impl TpmCredStore {
+    /// The public exponent to use for generated RSA keys.
+    const RSA_EXPONENT: u32 = 65537; // 2**16 + 1
+
+    const RSA_KEY_BITS: RsaKeyBits = RsaKeyBits::Rsa3072;
+
     pub(crate) fn new<P: AsRef<Path>>(
         mut context: Context, cookie_path: P
     ) -> Result<TpmCredStore> {
@@ -128,7 +140,6 @@ impl TpmCredStore {
     }
 
     fn gen_node_creds(&self) -> Result<TpmCreds> {
-        const RSA_EXPONENT: u32 = 65537; // 2**16 + 1
         let template = {
             let object_attributes = ObjectAttributes::builder()
                 .with_fixed_tpm(true)
@@ -146,8 +157,8 @@ impl TpmCredStore {
             let parameters = PublicRsaParameters::new(
                 SymmetricDefinitionObject::Null,
                 RsaScheme::Null,
-                RsaKeyBits::Rsa3072,
-                RsaExponent::try_from(RSA_EXPONENT).map_err(Error::from)?, 
+                TpmCredStore::RSA_KEY_BITS,
+                RsaExponent::try_from(TpmCredStore::RSA_EXPONENT).map_err(Error::from)?, 
             );
             let unique = PublicKeyRsa::try_from(self.cookie.as_slice())
                 .map_err(|e| Error::Message(e.to_string()))?;
@@ -307,7 +318,7 @@ impl Signer for TpmCreds {
         };
         let buf = match sig {
             tss_esapi::structures::Signature::RsaSsa(inner) => {
-                let mut buf = [0u8; RSA_SIG_LEN];
+                let mut buf = [0u8; RSA_KEY_BYTES];
                 let slice: &[u8] = inner.signature();
                 buf.as_mut_slice().write_all(slice).map_err(Error::from)?;
                 buf
@@ -320,8 +331,16 @@ impl Signer for TpmCreds {
 }
 
 impl Decrypter for TpmCreds {
-    fn decrypt(&self, _slice: &[u8]) -> Result<Vec<u8>> {
-      unimplemented!()  
+    fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
+        let cipher_text = PublicKeyRsa::try_from(slice).map_err(|e| Error::Message(e.to_string()))?;
+        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 plain_text = {
+            let mut lock = self.context.lock().map_err(|e| Error::Message(e.to_string()))?;
+            lock.rsa_decrypt(self.handle, cipher_text, in_scheme, label)?
+        };
+        Ok(Vec::from(plain_text.value()))
     } 
 }
 
@@ -413,6 +432,7 @@ mod test {
             PublicEccParameters,
         },
     };
+    use ctor::ctor;
 
     trait ContextExt {
         fn for_test() -> Result<Context>;
@@ -425,6 +445,11 @@ mod test {
         }
     }
 
+    #[ctor]
+    fn ctor() {
+        env_logger::init();
+    }
+
     /// Displays the message associated with a TSS2 return code.
     //#[test]
     fn print_error_message() {
@@ -558,18 +583,33 @@ mod test {
         assert_eq(MessageDigest::sha3_512(), Nid::sha3_512());
     }
 
-    #[test]
-    fn tpm_sign_verify() -> Result<()> {
-        env_logger::init();
+    fn test_store() -> Result<TpmCredStore> {
         let dir = TempDir::new("btnode").map_err(Error::from)?;
         let cookie_path = dir.path().join("cookie.bin");
         let store = TpmCredStore::new(Context::for_test()?, &cookie_path)?;       
+        dir.close()?;
+        Ok(store)
+    }
+
+    #[test]
+    fn tpm_sign_verify() -> Result<()> {
+        let store = test_store()?;
         let handle = store.gen_node_creds()?;
         let data: [u8; 1024] = rand_array()?;
         let parts = [data.as_slice()];
         let sig = handle.sign(parts.into_iter())?;
         assert!(handle.verify(parts.into_iter(), sig.as_slice())?);
-        dir.close()?;
+        Ok(())
+    }
+
+    #[test]
+    fn tpm_encrypt_decrypt() -> Result<()> {
+        let store = test_store()?;
+        let handle = store.gen_node_creds()?;
+        let expected: [u8; RSA_KEY_BYTES / 2] = rand_array()?;
+        let ct = handle.encrypt(expected.as_slice())?;
+        let actual = handle.decrypt(&ct)?;
+        assert_eq!(expected.as_slice(), actual);
         Ok(())
     }
 
@@ -578,4 +618,16 @@ mod test {
     fn hashcheck_null() {
         HashcheckTicket::null();
     }
+
+    /// Checks that the value of `TpmCredStore::RSA_KEY_BITS` matches the value of `RSA_KEY_BYTES`.
+    #[test]
+    fn rsa_key_bits_and_key_bytes_compatible() {
+        let bytes = match TpmCredStore::RSA_KEY_BITS {
+            RsaKeyBits::Rsa1024 => 128,
+            RsaKeyBits::Rsa2048 => 256,
+            RsaKeyBits::Rsa3072 => 384,
+            RsaKeyBits::Rsa4096 => 512,
+        };
+        assert_eq!(RSA_KEY_BYTES, bytes)
+    }
 }

+ 1 - 1
crates/btnode/src/test_helpers.rs

@@ -483,7 +483,7 @@ fn write_rsa_keys_to_file(path: &str) -> Result<()> {
     use openssl::{
         rsa::Rsa,
     };
-    let key_bits = 8 * u32::try_from(RSA_SIG_LEN).map_err(|e| Error::Message(e.to_string()))?;
+    let key_bits = 8 * u32::try_from(RSA_KEY_BYTES).map_err(|e| Error::Message(e.to_string()))?;
     let rsa = Rsa::generate(key_bits).map_err(convert_err)?;
     let public_der = rsa.public_key_to_der().map_err(convert_err)?;
     let private_der = rsa.private_key_to_der().map_err(convert_err)?;