Преглед изворни кода

Got the first integration test which writes a block to the filesystem
to pass.

Matthew Carr пре 2 година
родитељ
комит
cb74c42086

+ 21 - 0
Cargo.lock

@@ -131,6 +131,7 @@ dependencies = [
  "brotli",
  "btserde",
  "ctor",
+ "dbus",
  "env_logger",
  "foreign-types",
  "harness",
@@ -225,6 +226,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "dbus"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6"
+dependencies = [
+ "libc",
+ "libdbus-sys",
+ "winapi",
+]
+
 [[package]]
 name = "either"
 version = "1.8.0"
@@ -347,6 +359,15 @@ version = "0.2.133"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
 
+[[package]]
+name = "libdbus-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b"
+dependencies = [
+ "pkg-config",
+]
+
 [[package]]
 name = "libloading"
 version = "0.7.3"

+ 1 - 0
crates/btlib/Cargo.toml

@@ -29,3 +29,4 @@ ctor = "0.1.22"
 nix = "0.25.0"
 env_logger = "0.9.0"
 lazy_static = "1.4.0"
+dbus = "0.9.6"

+ 10 - 0
crates/btlib/TODO.txt

@@ -18,3 +18,13 @@ Add a ser_sign_into method to SignerExt which serializes a value into a provided
 a signature over this data.
 
 Convert all sector sizes to u64 for portability.
+
+Create an extension trait for u64 with a method for adding an i64 to it. Use this in
+SecretStream::seek and Trailered::seek.
+
+Create a full-duplex stream for compressing and decompressing data.
+
+Create a struct which digests data written to it before passing it to an underlying Write.
+
+Fix bug where writing to a block that already has a Writecap in its header using the creds of
+a different node produces an invalid signature (a signature using the creds of the other node).

+ 121 - 47
crates/btlib/src/crypto/mod.rs

@@ -1,11 +1,12 @@
 /// Functions for performing cryptographic operations on the main data structures.
+///
 pub mod tpm;
 
 use crate::{Block, BoxInIoErr, Decompose, HeaderAccess, Trailered, WriteInteg, SECTOR_SZ_DEFAULT};
 
 use super::{
-    fmt, io, BigArray, Deserialize, Display, Epoch, Formatter, Hashable, Header, Owned, Path,
-    Principal, Read, Sectored, Seek, Serialize, TryCompose, Write, Writecap,
+    fmt, io, BigArray, Deserialize, Display, Epoch, Formatter, Hashable, Header, Path, Principal,
+    Principaled, Read, Sectored, Seek, Serialize, TryCompose, Write, Writecap,
 };
 
 use btserde::{self, from_vec, to_vec, write_to};
@@ -878,11 +879,17 @@ impl<T> PKeyExt<T> for PKey<T> {
 
 /// Represents any kind of asymmetric key.
 #[derive(Debug, Clone)]
-pub struct AsymKey<P: KeyPrivacy, S: Scheme> {
+pub struct AsymKey<P, S> {
     scheme: S,
     pkey: PKey<P>,
 }
 
+impl<P, S: Copy> AsymKey<P, S> {
+    pub fn scheme(&self) -> S {
+        self.scheme
+    }
+}
+
 pub type AsymKeyPub<S> = AsymKey<Public, S>;
 
 impl<S: Scheme> AsymKey<Public, S> {
@@ -946,8 +953,8 @@ impl<S: Scheme> PartialEq for AsymKey<Public, S> {
     }
 }
 
-impl Owned for AsymKey<Public, Sign> {
-    fn owner_of_kind(&self, kind: HashKind) -> Principal {
+impl Principaled for AsymKey<Public, Sign> {
+    fn principal_of_kind(&self, kind: HashKind) -> Principal {
         let der = self.pkey.public_key_to_der().unwrap();
         let bytes = hash(kind.into(), der.as_slice()).unwrap();
         let mut hash_buf = Hash::from(kind);
@@ -1038,9 +1045,10 @@ impl<S: Scheme> AsymKeyPair<S> {
     }
 }
 
-impl Owned for AsymKeyPair<Sign> {
-    fn owner_of_kind(&self, kind: HashKind) -> Principal {
-        self.public.owner_of_kind(kind)
+// Note that only signing keys are associated with a Principal.
+impl Principaled for AsymKeyPair<Sign> {
+    fn principal_of_kind(&self, kind: HashKind) -> Principal {
+        self.public.principal_of_kind(kind)
     }
 }
 
@@ -1072,18 +1080,27 @@ impl Verifier for AsymKeyPair<Sign> {
 pub struct ConcreteCreds {
     sign: AsymKeyPair<Sign>,
     encrypt: AsymKeyPair<Encrypt>,
+    writecap: Option<Writecap>,
 }
 
 impl ConcreteCreds {
     pub(crate) fn new(sign: AsymKeyPair<Sign>, encrypt: AsymKeyPair<Encrypt>) -> ConcreteCreds {
-        ConcreteCreds { sign, encrypt }
+        ConcreteCreds {
+            sign,
+            encrypt,
+            writecap: None,
+        }
     }
 
     #[cfg(test)]
     pub(crate) fn generate() -> Result<ConcreteCreds> {
         let encrypt = Encrypt::RSA_OAEP_3072_SHA_256.generate()?;
         let sign = Sign::RSA_PSS_3072_SHA_256.generate()?;
-        Ok(ConcreteCreds { sign, encrypt })
+        Ok(ConcreteCreds {
+            sign,
+            encrypt,
+            writecap: None,
+        })
     }
 }
 
@@ -1099,9 +1116,9 @@ impl Encrypter for ConcreteCreds {
     }
 }
 
-impl Owned for ConcreteCreds {
-    fn owner_of_kind(&self, kind: HashKind) -> Principal {
-        self.sign.owner_of_kind(kind)
+impl Principaled for ConcreteCreds {
+    fn principal_of_kind(&self, kind: HashKind) -> Principal {
+        self.sign.principal_of_kind(kind)
     }
 }
 
@@ -1123,7 +1140,15 @@ impl Decrypter for ConcreteCreds {
     }
 }
 
-impl CredsPriv for ConcreteCreds {}
+impl CredsPriv for ConcreteCreds {
+    fn writecap(&self) -> Option<&Writecap> {
+        self.writecap.as_ref()
+    }
+
+    fn set_writecap(&mut self, writecap: Writecap) {
+        self.writecap = Some(writecap);
+    }
+}
 
 impl Creds for ConcreteCreds {}
 
@@ -1152,32 +1177,80 @@ pub trait Signer {
 pub trait SignerExt: Signer {
     fn ser_sign<T: Serialize>(&self, value: &T) -> Result<Signed<T>> {
         let data = to_vec(value)?;
-        let sig = self.sign([data.as_slice()].into_iter())?;
+        let sig = self.sign(std::iter::once(data.as_slice()))?;
         Ok(Signed::new(data, sig))
     }
 }
 
-pub(crate) trait Verifier {
+impl<T: Signer + ?Sized> SignerExt for T {}
+
+pub trait Verifier {
     fn verify<'a, I: Iterator<Item = &'a [u8]>>(&self, parts: I, signature: &[u8]) -> Result<()>;
 }
 
+pub trait VerifierExt: Verifier {
+    fn ser_verify<T: Serialize>(&self, value: &T, signature: &[u8]) -> Result<()> {
+        let data = to_vec(value)?;
+        self.verify(std::iter::once(data.as_slice()), signature)
+    }
+}
+
+impl<T: Verifier + ?Sized> VerifierExt for T {}
+
 /// Trait for types which can be used as public credentials.
-pub(crate) trait CredsPub: Verifier + Encrypter + Owned {
+pub(crate) trait CredsPub: Verifier + Encrypter + Principaled {
     /// Returns a reference to the public signing key which can be used to verify signatures.
     fn public_sign(&self) -> &AsymKey<Public, Sign>;
 }
 
 /// Trait for types which contain private credentials.
-pub(crate) trait CredsPriv: Decrypter + Signer {}
+pub(crate) trait CredsPriv: Decrypter + Signer {
+    /// Returns a reference to the writecap associated with these credentials, if one has been
+    /// issued.
+    fn writecap(&self) -> Option<&Writecap>;
+
+    /// Sets the writecap used by these creds to the given instance.
+    fn set_writecap(&mut self, writecap: Writecap);
+}
 
 /// Trait for types which contain both public and private credentials.
-pub(crate) trait Creds: CredsPriv + CredsPub {}
+pub(crate) trait Creds: CredsPriv + CredsPub {
+    fn issue_writecap(
+        &self,
+        issued_to: Principal,
+        path_components: Vec<String>,
+        expires: Epoch,
+    ) -> Result<Writecap> {
+        let path = crate::Path {
+            root: self.principal(),
+            components: path_components,
+        };
+        let signing_key = self.public_sign();
+        let signature = {
+            let sig_input = WritecapSigInput {
+                issued_to: &issued_to,
+                path: &path,
+                expires: &expires,
+                signing_key,
+            };
+            let signed = self.ser_sign(&sig_input)?;
+            signed.sig
+        };
+        Ok(Writecap {
+            issued_to,
+            path,
+            expires,
+            signing_key: signing_key.to_owned(),
+            signature,
+            next: self.writecap().map(|e| Box::new(e.to_owned())),
+        })
+    }
+}
 
 /// A trait for types which store credentials.
 pub(crate) trait CredStore {
     type CredHandle: Creds;
     type ExportedCreds: Serialize + for<'de> Deserialize<'de>;
-    type WritecapReq: Serialize + for<'de> Deserialize<'de> + AsRef<AsymKeyPub<Sign>>;
 
     /// Returns the node credentials. If credentials haven't been generated, they are generated
     /// stored and returned.
@@ -1200,15 +1273,6 @@ pub(crate) trait CredStore {
         password: &str,
         exported: Self::ExportedCreds,
     ) -> Result<Self::CredHandle>;
-    /// Creates a writecap request for the given `Principal`.
-    fn request_writecap(&self, root: Principal) -> Result<Self::WritecapReq>;
-    /// Issues a writecap for the given path to the node in the given request.
-    fn issue_writecap(
-        &self,
-        request: &Self::WritecapReq,
-        path: &Path,
-        password: &str,
-    ) -> Result<Writecap>;
 }
 
 /// Returns the base 2 logarithm of the given number. This function will return -1 when given 0, and
@@ -1482,11 +1546,11 @@ pub struct VecMerkleTree<T> {
 }
 
 impl<T> VecMerkleTree<T> {
-    /// A byte to prefix data being hashed for leaf nodes. It's important that this is different
+    /// A slice to prefix to data being hashed for leaf nodes. It's important that this is different
     /// from `INTERIOR_PREFIX`.
     const LEAF_PREFIX: &'static [u8] = b"Leaf";
-    /// A byte to prefix data being hashed for interior nodes. It's important that this is different
-    /// from 'LEAF_PREFIX`.
+    /// A slice to prefix to data being hashed for interior nodes. It's important that this is
+    /// different from 'LEAF_PREFIX`.
     const INTERIOR_PREFIX: &'static [u8] = b"Interior";
 
     /// Creates a new tree with no nodes in it and the given sector size.
@@ -1855,6 +1919,10 @@ impl<T: HeaderAccess> HeaderAccess for MerkleStream<T> {
     fn integrity(&self) -> Option<&[u8]> {
         self.trailered.inner.integrity()
     }
+
+    fn set_path(&mut self, path: Path) {
+        self.trailered.inner.set_path(path)
+    }
 }
 
 // A stream which encrypts all data written to it and decrypts all data read from it.
@@ -2026,6 +2094,10 @@ impl<T: HeaderAccess> HeaderAccess for SecretStream<T> {
     fn integrity(&self) -> Option<&[u8]> {
         self.inner.integrity()
     }
+
+    fn set_path(&mut self, path: Path) {
+        self.inner.set_path(path)
+    }
 }
 
 impl<T: Read + Write + Seek + HeaderAccess> Block for SecretStream<T> {}
@@ -2056,10 +2128,7 @@ pub(crate) fn verify_header(header: &Header, sig: &Signature) -> Result<()> {
         .as_ref()
         .ok_or(crate::Error::MissingWritecap)?;
     verify_writecap(writecap, &header.path)?;
-    let header_data = to_vec(&header)?;
-    writecap
-        .signing_key
-        .verify(std::iter::once(header_data.as_slice()), sig.as_slice())
+    header.signing_key.ser_verify(&header, sig.as_slice())
 }
 
 #[derive(Serialize)]
@@ -2110,7 +2179,7 @@ pub enum WritecapAuthzErr {
 pub(crate) fn verify_writecap(mut writecap: &Writecap, path: &Path) -> Result<()> {
     const CHAIN_LEN_LIMIT: usize = 256;
     let mut prev: Option<&Writecap> = None;
-    let mut sig_input = Vec::new();
+    let mut sig_input_buf = Vec::new();
     let now = Epoch::now();
     for _ in 0..CHAIN_LEN_LIMIT {
         if !writecap.path.contains(path) {
@@ -2120,15 +2189,20 @@ pub(crate) fn verify_writecap(mut writecap: &Writecap, path: &Path) -> Result<()
             return Err(WritecapAuthzErr::Expired.into());
         }
         if let Some(prev) = &prev {
-            if prev.signing_key.owner_of_kind(writecap.issued_to.kind()) != writecap.issued_to {
+            if prev
+                .signing_key
+                .principal_of_kind(writecap.issued_to.kind())
+                != writecap.issued_to
+            {
                 return Err(WritecapAuthzErr::NotChained.into());
             }
         }
-        let sig = WritecapSigInput::from(writecap);
-        sig_input.clear();
-        write_to(&sig, &mut sig_input).map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?;
+        let sig_input = WritecapSigInput::from(writecap);
+        sig_input_buf.clear();
+        write_to(&sig_input, &mut sig_input_buf)
+            .map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?;
         writecap.signing_key.verify(
-            [sig_input.as_slice()].into_iter(),
+            std::iter::once(sig_input_buf.as_slice()),
             writecap.signature.as_slice(),
         )?;
         match &writecap.next {
@@ -2139,7 +2213,7 @@ pub(crate) fn verify_writecap(mut writecap: &Writecap, path: &Path) -> Result<()
             None => {
                 // We're at the root key. As long as the signer of this writecap is the owner of
                 // the path, then the writecap is valid.
-                if writecap.signing_key.owner_of_kind(path.owner.kind()) == path.owner {
+                if writecap.signing_key.principal_of_kind(path.root.kind()) == path.root {
                     return Ok(());
                 } else {
                     return Err(WritecapAuthzErr::RootDoesNotOwnPath.into());
@@ -2250,7 +2324,7 @@ mod tests {
         let (mut root_writecap, root_key) = make_self_signed_writecap();
         root_writecap.issued_to = Principal(Hash::Sha2_256([0; 32]));
         sign_writecap(&mut root_writecap, &root_key)?;
-        let node_principal = NODE_CREDS.owner();
+        let node_principal = NODE_CREDS.principal();
         let writecap = make_writecap_trusted_by(
             root_writecap,
             &root_key,
@@ -2265,13 +2339,13 @@ mod tests {
     fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> {
         let (mut root_writecap, root_key) = make_self_signed_writecap();
         let owner = Principal(Hash::Sha2_256([0; 32]));
-        root_writecap.path = make_path_with_owner(owner, vec![]);
+        root_writecap.path = make_path_with_root(owner, vec![]);
         sign_writecap(&mut root_writecap, &root_key)?;
-        let node_owner = NODE_CREDS.owner();
+        let node_principal = NODE_CREDS.principal();
         let writecap = make_writecap_trusted_by(
             root_writecap,
             &root_key,
-            node_owner,
+            node_principal,
             vec!["apps", "contacts"],
         );
         let result = verify_writecap(&writecap, &writecap.path);

+ 132 - 109
crates/btlib/src/crypto/tpm.rs

@@ -18,6 +18,7 @@ use std::{
     os::{raw::c_char, unix::fs::PermissionsExt},
     path::{Path, PathBuf},
     sync::{Arc, RwLock, RwLockWriteGuard},
+    time::Duration,
 };
 use tss_esapi::{
     attributes::{object::ObjectAttributes, SessionAttributesBuilder},
@@ -104,6 +105,8 @@ trait ContextExt {
         auth: Auth,
         encryption_key: &AeadKey,
     ) -> Result<KeyPair<S>>;
+
+    fn set_auth(&mut self, cred_data: &CredData, password: &str) -> Result<()>;
 }
 
 impl ContextExt for Context {
@@ -337,6 +340,13 @@ impl ContextExt for Context {
             private: key_handle,
         })
     }
+
+    fn set_auth(&mut self, creds: &CredData, password: &str) -> Result<()> {
+        let auth = Auth::try_from(password.as_bytes())?;
+        self.tr_set_auth(creds.sign.private.into(), auth.clone())?;
+        self.tr_set_auth(creds.enc.private.into(), auth)?;
+        Ok(())
+    }
 }
 
 enum IsTrial {
@@ -447,7 +457,8 @@ fn deserialize_as_vec<'de, D: Deserializer<'de>, T: TryFrom<Vec<u8>>>(
         .map_err(|_| de::Error::custom("Unable to convert from vector"))
 }
 
-pub struct ExportedKeyPair<S: Scheme> {
+#[derive(Serialize, Deserialize)]
+pub struct ExportedKeyPair<S> {
     scheme: S,
     tagged_ct: TaggedCiphertext<TpmBlobs, PublicWrapper>,
 }
@@ -456,59 +467,12 @@ impl<S: Scheme> ExportedKeyPair<S> {
     const FIELDS: &'static [&'static str] = &["scheme", "tagged_ct"];
 }
 
-impl<S: Scheme> Serialize for ExportedKeyPair<S> {
-    fn serialize<T: Serializer>(&self, ser: T) -> std::result::Result<T::Ok, T::Error> {
-        let mut struct_ser = ser.serialize_struct(stringify!(ExportedKey), Self::FIELDS.len())?;
-        struct_ser.serialize_field(Self::FIELDS[0], &self.scheme)?;
-        struct_ser.serialize_field(Self::FIELDS[1], &self.tagged_ct)?;
-        struct_ser.end()
-    }
-}
-
-impl<'de, S: Scheme> Deserialize<'de> for ExportedKeyPair<S> {
-    fn deserialize<D: Deserializer<'de>>(de: D) -> std::result::Result<Self, D::Error> {
-        struct StructVisitor<S: Scheme>(PhantomData<S>);
-
-        impl<'de, S: Scheme> Visitor<'de> for StructVisitor<S> {
-            type Value = ExportedKeyPair<S>;
-
-            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-                formatter.write_fmt(format_args!("struct {}", stringify!(ExportedKey)))
-            }
-
-            fn visit_seq<V: SeqAccess<'de>>(
-                self,
-                mut seq: V,
-            ) -> std::result::Result<Self::Value, V::Error> {
-                fn conv_err<T, E: de::Error>(
-                    result: std::result::Result<T, tss_esapi::Error>,
-                ) -> std::result::Result<T, E> {
-                    result.map_err(de::Error::custom)
-                }
-
-                let scheme: S = seq
-                    .next_element()?
-                    .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[0]))?;
-                let tagged_ct: TaggedCiphertext<TpmBlobs, PublicWrapper> = seq
-                    .next_element()?
-                    .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[1]))?;
-                Ok(ExportedKeyPair { scheme, tagged_ct })
-            }
-        }
-
-        de.deserialize_struct(
-            stringify!(ExportedKey),
-            Self::FIELDS,
-            StructVisitor(PhantomData),
-        )
-    }
-}
-
 #[derive(Serialize, Deserialize)]
 pub struct ExportedCreds {
     sign: ExportedKeyPair<Sign>,
     enc: ExportedKeyPair<Encrypt>,
     params: DerivationParams,
+    writecap: Option<Writecap>,
 }
 
 /// A public/private key pair in a form which can be serialized and deserialized.
@@ -518,11 +482,9 @@ struct StoredKeyPair<S: Scheme> {
     private: TPM2_HANDLE,
 }
 
-// I was unable to use `derive(Deserialize)` on `StoredKeyPair`, so I resorted to implementing it
-// manually.
 impl<'de, S: Scheme> Deserialize<'de> for StoredKeyPair<S> {
     fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
-        const FIELDS: &[&str] = &["public", "private"];
+        const FIELDS: &[&str] = &["public", "private", "writecap"];
 
         struct StructVisitor<S: Scheme>(PhantomData<S>);
 
@@ -561,13 +523,18 @@ impl<'de, S: Scheme> Deserialize<'de> for StoredKeyPair<S> {
 struct StoredCredData {
     sign: StoredKeyPair<Sign>,
     enc: StoredKeyPair<Encrypt>,
+    writecap: Option<Writecap>,
 }
 
 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(CredData { sign, enc })
+        Ok(CredData {
+            sign,
+            enc,
+            writecap: self.writecap.clone(),
+        })
     }
 }
 
@@ -856,6 +823,7 @@ impl<S: Scheme> KeyPair<S> {
 struct CredData {
     sign: KeyPair<Sign>,
     enc: KeyPair<Encrypt>,
+    writecap: Option<Writecap>,
 }
 
 struct State {
@@ -904,6 +872,7 @@ pub(crate) struct TpmCredStore {
 impl TpmCredStore {
     const SIGN_SCHEME: Sign = Sign::RSA_PSS_2048_SHA_256;
     const ENCRYPT_SCHEME: Encrypt = Encrypt::RSA_OAEP_2048_SHA_256;
+    const DEFAULT_WRITECAP_EXP: Duration = Duration::from_secs(60 * 60 * 24 * 365 * 10);
 
     pub(crate) fn new<P: AsRef<Path>>(mut context: Context, state_path: P) -> Result<TpmCredStore> {
         let storage = Storage::load_or_init(state_path.as_ref())?;
@@ -949,6 +918,7 @@ impl TpmCredStore {
         let handles = StoredCredData {
             sign: creds.sign.to_stored(sign_handle),
             enc: creds.enc.to_stored(enc_handle),
+            writecap: creds.writecap.clone(),
         };
         update_storage(&mut guard.storage, handles);
         match self.save_storage(&mut guard) {
@@ -1029,7 +999,11 @@ impl TpmCredStore {
     fn gen_node_creds(&self) -> Result<TpmCreds> {
         let sign = self.gen_node_sign_key()?;
         let enc = self.gen_node_enc_key()?;
-        let cred_data = CredData { sign, enc };
+        let cred_data = CredData {
+            sign,
+            enc,
+            writecap: None,
+        };
         let creds = TpmCreds::new(cred_data, &self.state);
         self.persist(&creds, |storage, handles| storage.node = Some(handles))?;
         Ok(creds)
@@ -1100,28 +1074,9 @@ impl DerivationParams {
     }
 }
 
-#[derive(Serialize, Deserialize)]
-struct WritecapReqBody {
-    pub_key: AsymKeyPub<Sign>,
-    req_for: Principal,
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct WritecapReq {
-    body: WritecapReqBody,
-    sig: Signature,
-}
-
-impl AsRef<AsymKeyPub<Sign>> for WritecapReq {
-    fn as_ref(&self) -> &AsymKeyPub<Sign> {
-        &self.body.pub_key
-    }
-}
-
 impl CredStore for TpmCredStore {
     type CredHandle = TpmCreds;
     type ExportedCreds = ExportedCreds;
-    type WritecapReq = WritecapReq;
 
     fn node_creds(&self) -> Result<TpmCreds> {
         {
@@ -1145,13 +1100,7 @@ impl CredStore for TpmCredStore {
         };
         let mut guard = self.state.write()?;
         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)?;
+        guard.context.set_auth(&key_handles, password)?;
         Ok(TpmCreds::new(key_handles, &self.state))
     }
 
@@ -1168,8 +1117,17 @@ impl CredStore for TpmCredStore {
         };
         let sign = self.gen_root_sign_key(password, policy.clone())?;
         let enc = self.gen_root_enc_key(password, policy)?;
-        let cred_data = CredData { sign, enc };
-        let creds = TpmCreds::new(cred_data, &self.state);
+        let cred_data = CredData {
+            sign,
+            enc,
+            writecap: None,
+        };
+        {
+            let mut guard = self.state.write()?;
+            guard.context.set_auth(&cred_data, password)?;
+        }
+        let mut creds = TpmCreds::new(cred_data, &self.state);
+        creds.init_root_writecap(Epoch::now() + Self::DEFAULT_WRITECAP_EXP)?;
         self.persist(&creds, |storage, handles| storage.root = Some(handles))?;
         Ok(creds)
     }
@@ -1212,7 +1170,12 @@ impl CredStore for TpmCredStore {
             policy_session,
             &aead_key,
         )?;
-        Ok(ExportedCreds { sign, enc, params })
+        Ok(ExportedCreds {
+            sign,
+            enc,
+            params,
+            writecap: root_creds.writecap.clone(),
+        })
     }
 
     fn import_root_creds(&self, password: &str, exported: ExportedCreds) -> Result<TpmCreds> {
@@ -1231,28 +1194,16 @@ impl CredStore for TpmCredStore {
                 guard
                     .context
                     .import_key(exported.enc, storage_key.private, auth, &aead_key)?;
-            let cred_data = CredData { sign, enc };
+            let cred_data = CredData {
+                sign,
+                enc,
+                writecap: exported.writecap,
+            };
             TpmCreds::new(cred_data, &self.state)
         };
         self.persist(&creds, |storage, handles| storage.root = Some(handles))?;
         Ok(creds)
     }
-
-    fn request_writecap(&self, req_for: Principal) -> Result<Self::WritecapReq> {
-        let node_creds = self.node_creds()?;
-        let pub_key = node_creds.public_sign().clone();
-        let body = WritecapReqBody { pub_key, req_for };
-        unimplemented!()
-    }
-
-    fn issue_writecap(
-        &self,
-        request: &Self::WritecapReq,
-        path: &crate::Path,
-        password: &str,
-    ) -> Result<Writecap> {
-        unimplemented!()
-    }
 }
 
 impl<S: Scheme> AsymKeyPub<S> {
@@ -1380,6 +1331,7 @@ pub(crate) struct TpmCreds {
     state: Arc<RwLock<State>>,
     sign: KeyPair<Sign>,
     enc: KeyPair<Encrypt>,
+    writecap: Option<Writecap>,
 }
 
 impl TpmCreds {
@@ -1388,13 +1340,20 @@ impl TpmCreds {
             sign: key_handles.sign,
             enc: key_handles.enc,
             state: state.clone(),
+            writecap: key_handles.writecap,
         }
     }
+
+    fn init_root_writecap(&mut self, expires: Epoch) -> Result<()> {
+        let writecap = self.issue_writecap(self.principal(), Vec::new(), expires)?;
+        self.writecap = Some(writecap);
+        Ok(())
+    }
 }
 
-impl Owned for TpmCreds {
-    fn owner_of_kind(&self, kind: HashKind) -> Principal {
-        self.sign.public.owner_of_kind(kind)
+impl Principaled for TpmCreds {
+    fn principal_of_kind(&self, kind: HashKind) -> Principal {
+        self.sign.public.principal_of_kind(kind)
     }
 }
 
@@ -1471,7 +1430,15 @@ impl Decrypter for TpmCreds {
     }
 }
 
-impl CredsPriv for TpmCreds {}
+impl CredsPriv for TpmCreds {
+    fn writecap(&self) -> Option<&Writecap> {
+        self.writecap.as_ref()
+    }
+
+    fn set_writecap(&mut self, writecap: Writecap) {
+        self.writecap = Some(writecap)
+    }
+}
 
 impl Creds for TpmCreds {}
 
@@ -1749,30 +1716,86 @@ mod test {
         let (harness, store) = test_store()?;
         let expected = {
             let creds = store.node_creds()?;
-            creds.owner()
+            creds.principal()
         };
         drop(store);
         let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
         let creds = store.node_creds()?;
-        let actual = creds.owner();
+        let actual = creds.principal();
         assert_eq!(expected, actual);
         sign_verify_test(&creds)?;
         encrypt_decrypt_test(&creds)?;
         Ok(())
     }
 
+    #[test]
+    fn root_key_can_be_used_after_generation() {
+        let (_harness, store) = test_store().expect("failed to make test store");
+        let creds = store
+            .gen_root_creds(&"TimeInvariant")
+            .expect("failed to gen root creds");
+        let data = [1u8; 32];
+        creds
+            .sign(std::iter::once(data.as_slice()))
+            .expect("sign failed");
+    }
+
+    #[test]
+    fn root_and_node_keys_generated() {
+        let (_harness, store) = test_store().expect("failed to make test store");
+        let _root_creds = store
+            .gen_root_creds(&"TranslationInvariant")
+            .expect("failed to gen root creds");
+        let _node_creds = store.node_creds().expect("failed to gen node creds");
+    }
+
+    #[test]
+    fn verify_root_writecap() {
+        let (_harness, store) = test_store().expect("failed to make test store");
+        let root_creds = store
+            .gen_root_creds(&"TranslationInvariant")
+            .expect("failed to gen root creds");
+        let writecap = root_creds.writecap().expect("no root writecap was present");
+        let path = crate::Path {
+            root: root_creds.principal(),
+            components: Vec::new(),
+        };
+        verify_writecap(&writecap, &path).expect("failed to verify root writecap");
+    }
+
+    #[test]
+    fn issue_writecap_to_node() {
+        let (_harness, store) = test_store().expect("failed to make test store");
+        let root_creds = store
+            .gen_root_creds(&"TranslationInvariant")
+            .expect("failed to gen root creds");
+        let path = crate::Path {
+            root: root_creds.principal(),
+            components: vec!["apps".to_string(), "comms".to_string()],
+        };
+        let node_creds = store.node_creds().expect("failed to gen node creds");
+        let writecap = root_creds
+            .issue_writecap(
+                node_creds.principal(),
+                path.components.clone(),
+                Epoch::now() + Duration::from_secs(3600),
+            )
+            .expect("failed to issue writecap");
+        verify_writecap(&writecap, &path).expect("failed to verify writecap");
+    }
+
     #[test]
     fn root_key_persisted() -> Result<()> {
         const PASSWORD: &str = "Scaramouch";
         let (harness, store) = test_store()?;
         let expected = {
             let creds = store.gen_root_creds(PASSWORD)?;
-            creds.owner()
+            creds.principal()
         };
         drop(store);
         let store = TpmCredStore::new(harness.context()?, harness.state_path())?;
         let creds = store.root_creds(PASSWORD)?;
-        let actual = creds.owner();
+        let actual = creds.principal();
         assert_eq!(expected, actual);
         sign_verify_test(&creds)?;
         encrypt_decrypt_test(&creds)?;
@@ -1780,7 +1803,7 @@ mod test {
     }
 
     #[test]
-    fn root_key_not_returned_when_password_wrong() -> Result<()> {
+    fn root_key_unusable_when_password_wrong() -> Result<()> {
         let (harness, store) = test_store()?;
         store.gen_root_creds("Galileo")?;
         drop(store);

+ 71 - 25
crates/btlib/src/lib.rs

@@ -2,6 +2,8 @@
 // TODO: Delete this prior to release.
 #![allow(dead_code)]
 
+mod crypto;
+
 #[cfg(test)]
 mod test_helpers;
 
@@ -17,7 +19,6 @@ extern crate lazy_static;
 
 use brotli::{CompressorWriter, Decompressor};
 use btserde::{self, read_from, write_to};
-mod crypto;
 use crypto::{
     AsymKeyPub, Ciphertext, Creds, Decrypter, Encrypter, EncrypterExt, Hash, HashKind,
     MerkleStream, SecretStream, Sign, Signature, Signer, SymKey, SymKeyKind,
@@ -182,6 +183,7 @@ pub trait HeaderAccess {
     fn add_readcap_for(&mut self, owner: Principal, key: &dyn Encrypter) -> Result<()>;
     /// Returns the integrity value used to protect the contents of the block.
     fn integrity(&self) -> Option<&[u8]>;
+    fn set_path(&mut self, path: Path);
 }
 
 /// Extensions to the `Read` trait.
@@ -217,21 +219,43 @@ trait ReadExt: Read {
 
 impl<T: Read> ReadExt for T {}
 
-#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
 pub struct Header {
     path: Path,
     readcaps: HashMap<Principal, Ciphertext<SymKey>>,
     writecap: Option<Writecap>,
     /// A hash which provides integrity for the contents of the block body.
     integrity: Option<Hash>,
+    /// The public key that corresponds to the private key used to sign this header.
+    signing_key: AsymKeyPub<Sign>,
 }
 
-#[derive(Serialize, Deserialize, Default)]
+impl Header {
+    fn new<C: Creds>(creds: &C) -> Header {
+        Header {
+            path: Path::default(),
+            readcaps: HashMap::new(),
+            writecap: creds.writecap().map(|e| e.to_owned()),
+            integrity: None,
+            signing_key: creds.public_sign().to_owned(),
+        }
+    }
+}
+
+#[derive(Serialize, Deserialize)]
 struct BlockTrailer {
     header: Header,
     sig: Signature,
 }
 
+impl BlockTrailer {
+    fn new<C: Creds>(creds: &C) -> BlockTrailer {
+        let header = Header::new(creds);
+        let sig = Signature::empty(header.signing_key.scheme());
+        BlockTrailer { header, sig }
+    }
+}
+
 struct BlockStream<T, C> {
     trailered: Trailered<T, BlockTrailer>,
     trailer: BlockTrailer,
@@ -248,13 +272,13 @@ impl<T: Read + Seek, C: Creds> BlockStream<T, C> {
                 trailer
             }
             None => {
-                let mut trailer = BlockTrailer::default();
+                let mut trailer = BlockTrailer::new(&creds);
                 let block_key = SymKey::generate(SymKeyKind::default())?;
                 trailer
                     .header
                     .readcaps
-                    .insert(creds.owner(), creds.ser_encrypt(&block_key)?);
-                // TODO: Insert writecap.
+                    .insert(creds.principal(), creds.ser_encrypt(&block_key)?);
+                trailer.header.writecap = creds.writecap().map(|e| e.to_owned());
                 trailer
             }
         };
@@ -316,13 +340,13 @@ impl<T: Seek, C> Seek for BlockStream<T, C> {
     }
 }
 
-impl<T, C: Decrypter + Owned> HeaderAccess for BlockStream<T, C> {
+impl<T, C: Decrypter + Principaled> HeaderAccess for BlockStream<T, C> {
     fn block_key(&self) -> Result<SymKey> {
         let readcap = self
             .trailer
             .header
             .readcaps
-            .get(&self.creds.owner())
+            .get(&self.creds.principal())
             .ok_or(Error::Crypto(crypto::Error::NoReadCap))?;
         Ok(crypto::decrypt(readcap, &self.creds)?)
     }
@@ -341,6 +365,10 @@ impl<T, C: Decrypter + Owned> HeaderAccess for BlockStream<T, C> {
             .as_ref()
             .map(|hash| hash.as_ref())
     }
+
+    fn set_path(&mut self, path: Path) {
+        self.trailer.header.path = path;
+    }
 }
 
 struct BlockOpenOptions<T, C> {
@@ -881,6 +909,10 @@ impl<T: HeaderAccess> HeaderAccess for SectoredBuf<T> {
     fn integrity(&self) -> Option<&[u8]> {
         self.inner.integrity()
     }
+
+    fn set_path(&mut self, path: Path) {
+        self.inner.set_path(path)
+    }
 }
 
 impl<T: Read + Write + Seek + HeaderAccess> Block for SectoredBuf<T> {}
@@ -989,20 +1021,20 @@ impl Principal {
 }
 
 /// Trait for types which are owned by a `Principal`.
-trait Owned {
+trait Principaled {
     /// Returns the `Principal` that owns `self`, using the given hash algorithm.
-    fn owner_of_kind(&self, kind: HashKind) -> Principal;
+    fn principal_of_kind(&self, kind: HashKind) -> Principal;
 
     /// Returns the `Principal` that owns `self`, using the default hash algorithm.
-    fn owner(&self) -> Principal {
-        self.owner_of_kind(HashKind::default())
+    fn principal(&self) -> Principal {
+        self.principal_of_kind(HashKind::default())
     }
 }
 
 /// An identifier for a block in a tree.
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
-struct Path {
-    owner: Principal,
+pub struct Path {
+    root: Principal,
     components: Vec<String>,
 }
 
@@ -1055,7 +1087,7 @@ impl Path {
 
     /// Returns true if `other` is a subpath of this `Path`.
     fn contains(&self, other: &Path) -> bool {
-        if self.owner != other.owner {
+        if self.root != other.root {
             return false;
         };
         // This path must be no longer than the other path.
@@ -1098,7 +1130,7 @@ impl<'s> TryFrom<&'s str> for Path {
         let hash =
             Hash::try_from(leading.as_str()).map_err(|_| PathError::InvalidLeadingComponent)?;
         Ok(Path {
-            owner: Principal(hash),
+            root: Principal(hash),
             components,
         })
     }
@@ -1121,7 +1153,7 @@ impl Display for Path {
 
 /// Errors which can occur when converting a string to a `Path`.
 #[derive(Debug, PartialEq)]
-enum PathError {
+pub enum PathError {
     /// Occurs when the number of bytes in a string is greater than `Path::BYTE_LIMIT`.
     PathTooLong(usize),
     /// Indicates that a path string was empty.
@@ -1187,7 +1219,7 @@ struct FragmentSerial(u32);
 mod tests {
     use std::io::Cursor;
 
-    use crate::crypto::{tpm::TpmCredStore, CredStore};
+    use crate::crypto::{tpm::TpmCredStore, CredStore, CredsPriv};
 
     use super::*;
     use tempdir::TempDir;
@@ -1205,7 +1237,7 @@ mod tests {
     #[test]
     fn path_from_str_multiple_components_ok() -> std::result::Result<(), PathError> {
         let expected = make_path(vec!["red", "green", "blue"]);
-        let input = format!("{}/red/green/blue", expected.owner.0);
+        let input = format!("{}/red/green/blue", expected.root.0);
         path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
@@ -1213,7 +1245,7 @@ mod tests {
     #[test]
     fn path_from_str_one_component_ok() -> std::result::Result<(), PathError> {
         let expected = make_path(vec![]);
-        let input = expected.owner.0.to_string();
+        let input = expected.root.0.to_string();
         path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
@@ -1222,7 +1254,7 @@ mod tests {
     fn path_from_str_trailing_slash_ok() -> std::result::Result<(), PathError> {
         // Notice the empty component at the end of this path due to the trailing slash.
         let expected = make_path(vec!["orange", "banana", "shotgun", ""]);
-        let input = format!("{}/orange/banana/shotgun/", expected.owner.0);
+        let input = format!("{}/orange/banana/shotgun/", expected.root.0);
         path_from_str_test_case(Ok(expected), input.as_str())?;
         Ok(())
     }
@@ -1292,7 +1324,7 @@ mod tests {
     fn path_contains_false_different_owners() {
         let first = make_path(vec!["apps"]);
         let mut second = make_path(vec!["apps"]);
-        second.owner = Principal(Hash::Sha2_256(PRINCIPAL2));
+        second.root = Principal(Hash::Sha2_256(PRINCIPAL2));
         assert!(!first.contains(&second));
     }
 
@@ -1659,13 +1691,26 @@ mod tests {
     #[test]
     fn block_contents_persisted() {
         const EXPECTED: &[u8] = b"Silly sordid sulking sultans.";
+        const PASSWORD: &str = "(1337Prestidigitation7331)";
         let temp_dir = TempDir::new("btlib").expect("failed to create temp dir");
         let file_path = temp_dir.path().join("test.blk").to_owned();
         let harness = SwtpmHarness::new().expect("failed to start swtpm");
         let context = harness.context().expect("failed to retrieve context");
         let cred_store = TpmCredStore::new(context, harness.state_path())
             .expect("failed to create TpmCredStore");
-        let creds = cred_store.node_creds().expect("failed to get node creds");
+        let root_creds = cred_store
+            .gen_root_creds(PASSWORD)
+            .expect("failed to get root creds");
+        let mut node_creds = cred_store.node_creds().expect("failed to get node creds");
+        let writecap = root_creds
+            .issue_writecap(
+                node_creds.principal(),
+                vec!["nodes".to_string(), "phone".to_string()],
+                Epoch::now() + Duration::from_secs(3600),
+            )
+            .expect("failed to issue writecap");
+        let path = writecap.path.clone();
+        node_creds.set_writecap(writecap);
         {
             let file = OpenOptions::new()
                 .create_new(true)
@@ -1675,10 +1720,11 @@ mod tests {
                 .expect("failed to open file");
             let mut block = BlockOpenOptions::new()
                 .with_inner(file)
-                .with_creds(creds.clone())
+                .with_creds(node_creds.clone())
                 .with_encrypt(true)
                 .open()
                 .expect("failed to open block");
+            block.set_path(path);
             block.write(EXPECTED).expect("failed to write");
             block.flush().expect("flush failed");
         }
@@ -1688,7 +1734,7 @@ mod tests {
             .expect("failed to reopen file");
         let mut block = BlockOpenOptions::new()
             .with_inner(file)
-            .with_creds(creds)
+            .with_creds(node_creds)
             .with_encrypt(true)
             .open()
             .expect("failed to reopen block");

+ 125 - 16
crates/btlib/src/test_helpers.rs

@@ -12,13 +12,16 @@ use std::{
     fs::File,
     io::{Cursor, Write},
     path::PathBuf,
-    process::{Command, ExitStatus},
+    process::{Child, Command, ExitStatus, Stdio},
     str::FromStr,
-    sync::atomic::{AtomicU16, Ordering},
+    sync::{
+        atomic::{AtomicU16, Ordering},
+        mpsc::{channel, Receiver, TryRecvError},
+    },
 };
 use tempdir::TempDir;
 use tss_esapi::{
-    tcti_ldr::{NetworkTPMConfig, TctiNameConf},
+    tcti_ldr::{TabrmdConfig, TctiNameConf},
     Context,
 };
 
@@ -99,17 +102,17 @@ pub(crate) fn make_principal() -> Principal {
     Principal(Hash::Sha2_256(PRINCIPAL))
 }
 
-pub(crate) fn make_path_with_owner(owner: Principal, rel_components: Vec<&str>) -> Path {
+pub(crate) fn make_path_with_root(root: Principal, rel_components: Vec<&str>) -> Path {
     let mut components = Vec::with_capacity(rel_components.len() + 1);
-    components.push(owner.0.to_string());
+    components.push(root.0.to_string());
     for component in rel_components {
         components.push(component.to_string());
     }
-    Path { owner, components }
+    Path { root, components }
 }
 
 pub(crate) fn make_path(rel_components: Vec<&str>) -> Path {
-    make_path_with_owner(make_principal(), rel_components)
+    make_path_with_root(make_principal(), rel_components)
 }
 
 pub(crate) fn make_writecap_and_creds(rel_components: Vec<&str>) -> (Writecap, impl Creds) {
@@ -135,7 +138,7 @@ pub(crate) fn make_writecap_trusted_by<C: Creds>(
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
         issued_to,
-        path: make_path_with_owner(next.path.owner.clone(), path_components),
+        path: make_path_with_root(next.path.root.clone(), path_components),
         expires: hour_hence,
         signing_key: trusting_creds.public_sign().clone(),
         signature: Signature::empty(Sign::RSA_PSS_3072_SHA_256),
@@ -157,11 +160,11 @@ pub(crate) fn make_self_signed_writecap() -> (Writecap, impl Creds) {
 }
 
 pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Writecap {
-    let root_principal = key.owner();
+    let root_principal = key.principal();
     let hour_hence = Epoch::now() + Duration::from_secs(3600);
     let mut writecap = Writecap {
         issued_to: root_principal.clone(),
-        path: make_path_with_owner(root_principal, vec![]),
+        path: make_path_with_root(root_principal, vec![]),
         expires: hour_hence,
         signing_key: key.public_sign().clone(),
         signature: Signature::empty(Sign::RSA_PSS_3072_SHA_256),
@@ -177,9 +180,9 @@ pub(crate) fn make_readcap() -> Readcap {
     make_readcap_for(&*ROOT_CREDS)
 }
 
-pub(crate) fn make_readcap_for<C: Encrypter + Owned>(creds: &C) -> Readcap {
+pub(crate) fn make_readcap_for<C: Encrypter + Principaled>(creds: &C) -> Readcap {
     Readcap {
-        issued_to: creds.owner(),
+        issued_to: creds.principal(),
         key: crypto::encrypt(&BLOCK_KEY, creds).expect("failed to encrypt block key"),
     }
 }
@@ -196,10 +199,11 @@ pub(crate) fn make_block_with(readcap: Readcap) -> Box<dyn Block> {
     let (writecap, creds) = make_writecap_and_creds(vec!["apps"]);
     let root_writecap = writecap.next.as_ref().unwrap();
     let header = Header {
-        path: make_path_with_owner(root_writecap.issued_to.clone(), vec!["apps", "verse"]),
+        path: make_path_with_root(root_writecap.issued_to.clone(), vec!["apps", "verse"]),
         readcaps,
         writecap: Some(writecap),
         integrity: Some(Hash::Sha2_256([0u8; HashKind::Sha2_256.len()])),
+        signing_key: creds.public_sign().to_owned(),
     };
     let sig = Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE);
     let mut stream =
@@ -519,16 +523,105 @@ impl ExitStatusExt for ExitStatus {
     }
 }
 
+/// A DBus message which is sent when the ownership of a name changes.
+struct NameOwnerChanged {
+    name: String,
+    old_owner: String,
+    new_owner: String,
+}
+
+impl dbus::arg::AppendAll for NameOwnerChanged {
+    fn append(&self, iter: &mut dbus::arg::IterAppend) {
+        dbus::arg::RefArg::append(&self.name, iter);
+        dbus::arg::RefArg::append(&self.old_owner, iter);
+        dbus::arg::RefArg::append(&self.new_owner, iter);
+    }
+}
+
+impl dbus::arg::ReadAll for NameOwnerChanged {
+    fn read(iter: &mut dbus::arg::Iter) -> std::result::Result<Self, dbus::arg::TypeMismatchError> {
+        Ok(NameOwnerChanged {
+            name: iter.read()?,
+            old_owner: iter.read()?,
+            new_owner: iter.read()?,
+        })
+    }
+}
+
+impl dbus::message::SignalArgs for NameOwnerChanged {
+    const NAME: &'static str = "NameOwnerChanged";
+    const INTERFACE: &'static str = "org.freedesktop.DBus";
+}
+
+/// A struct used to block until a specific name appears on DBus.
+struct DbusBlocker {
+    receiver: Receiver<()>,
+    conn: dbus::blocking::Connection,
+}
+
+impl DbusBlocker {
+    fn new_session(name: String) -> Result<DbusBlocker> {
+        use dbus::{blocking::Connection, Message};
+        const DEST: &str = "org.freedesktop.DBus";
+
+        let (sender, receiver) = channel();
+        let conn = Connection::new_session().map_err(Error::custom)?;
+        let proxy = conn.with_proxy(DEST, "/org/freedesktop/DBus", Duration::from_secs(1));
+        let _ = proxy.match_signal(move |h: NameOwnerChanged, _: &Connection, _: &Message| {
+            let name_appeared = h.name == name;
+            if name_appeared {
+                if let Err(err) = sender.send(()) {
+                    error!("failed to send unblocking signal: {err}");
+                }
+            }
+            let remove_match = !name_appeared;
+            remove_match
+        });
+        Ok(DbusBlocker { receiver, conn })
+    }
+
+    fn block(&mut self, timeout: Duration) -> Result<()> {
+        let time_limit = SystemTime::now() + timeout;
+        loop {
+            self.conn
+                .process(Duration::from_millis(100))
+                .map_err(Error::custom)?;
+            match self.receiver.try_recv() {
+                Ok(_) => break,
+                Err(err) => match err {
+                    TryRecvError::Empty => (),
+                    _ => return Err(Error::custom(err)),
+                },
+            }
+            if SystemTime::now() > time_limit {
+                return Err(Error::custom("timed out"));
+            }
+        }
+        Ok(())
+    }
+}
+
 pub(crate) struct SwtpmHarness {
     dir: TempDir,
     port: u16,
     state_path: PathBuf,
     pid_path: PathBuf,
+    tabrmd: Child,
 }
 
 impl SwtpmHarness {
     const HOST: &'static str = "127.0.0.1";
 
+    fn dbus_name(port: u16) -> String {
+        let port_str: String = port
+            .to_string()
+            .chars()
+            // Shifting each code point by 17 makes the digits into capital letters.
+            .map(|e| ((e as u8) + 17) as char)
+            .collect();
+        format!("com.intel.tss2.Tabrmd.{port_str}")
+    }
+
     pub(crate) fn new() -> crypto::Result<SwtpmHarness> {
         static PORT: AtomicU16 = AtomicU16::new(21901);
         let port = PORT.fetch_add(2, Ordering::SeqCst);
@@ -539,6 +632,7 @@ impl SwtpmHarness {
         let conf_path = dir_path.join("swtpm_setup.conf");
         let state_path = dir_path.join("state.bt");
         let pid_path = dir_path.join("swtpm.pid");
+        let dbus_name = Self::dbus_name(port);
         let addr = Self::HOST;
         std::fs::write(
             &conf_path,
@@ -549,6 +643,7 @@ active_pcr_banks = sha256
 "#,
         )?;
         Command::new("swtpm_setup")
+            .stdout(Stdio::null())
             .args([
                 "--tpm2",
                 "--config",
@@ -578,18 +673,29 @@ active_pcr_banks = sha256
             ])
             .status()?
             .success_or_err()?;
+        let mut blocker = DbusBlocker::new_session(dbus_name.clone())?;
+        let tabrmd = Command::new("tpm2-abrmd")
+            .args([
+                format!("--tcti=swtpm:host=127.0.0.1,port={port}").as_str(),
+                "--dbus-name",
+                dbus_name.as_str(),
+                "--session",
+            ])
+            .spawn()?;
+        blocker.block(Duration::from_secs(5))?;
         Ok(SwtpmHarness {
             dir,
             port,
             state_path,
             pid_path,
+            tabrmd,
         })
     }
 
     pub(crate) fn context(&self) -> crypto::Result<Context> {
-        let config_string = format!("host={},port={}", Self::HOST, self.port);
-        let config = NetworkTPMConfig::from_str(config_string.as_str())?;
-        Ok(Context::new(TctiNameConf::Swtpm(config))?)
+        let config_string = format!("bus_name={},bus_type=session", Self::dbus_name(self.port));
+        let config = TabrmdConfig::from_str(config_string.as_str())?;
+        Ok(Context::new(TctiNameConf::Tabrmd(config))?)
     }
 
     pub(crate) fn dir_path(&self) -> &std::path::Path {
@@ -603,6 +709,9 @@ active_pcr_banks = sha256
 
 impl Drop for SwtpmHarness {
     fn drop(&mut self) {
+        if let Err(err) = self.tabrmd.kill() {
+            error!("failed to kill tpm2-abrmd: {err}");
+        }
         let pid_str = std::fs::read_to_string(&self.pid_path).unwrap();
         let pid_int = pid_str.parse::<i32>().unwrap();
         let pid = Pid::from_raw(pid_int);

+ 8 - 0
crates/btserde/src/error.rs

@@ -15,6 +15,7 @@ pub enum Error {
     NotSupported(&'static str),
     InvalidUtf8Char,
     Format(std::fmt::Error),
+    Custom(Box<dyn std::error::Error + Send + Sync>),
 }
 
 impl std::error::Error for Error {}
@@ -40,10 +41,17 @@ impl Display for Error {
             }
             Error::InvalidUtf8Char => formatter.write_str("Invalid UTF-8 character encountered."),
             Error::Format(fmt_error) => fmt_error.fmt(formatter),
+            Error::Custom(err) => err.fmt(formatter),
         }
     }
 }
 
+impl Error {
+    pub fn custom<E: Into<Box<dyn std::error::Error + Send + Sync>> + 'static>(err: E) -> Error {
+        Error::Custom(err.into())
+    }
+}
+
 impl ser::Error for Error {
     fn custom<T: Display>(message: T) -> Self {
         Error::Message(message.to_string())