From 810d2cad2c7ad90038e5db575c33f16f294056e8 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Fri, 10 Jul 2026 15:25:53 -0500 Subject: [PATCH] common: Remove split between model and server types --- credentialsd-common/src/lib.rs | 1 - credentialsd-common/src/model.rs | 656 +++++++++++++++++- credentialsd-common/src/server.rs | 642 ----------------- credentialsd-ui/src/client.rs | 3 +- credentialsd-ui/src/dbus.rs | 5 +- credentialsd-ui/src/gui/mod.rs | 2 +- .../src/gui/view_model/gtk/application.rs | 4 +- credentialsd-ui/src/gui/view_model/gtk/mod.rs | 2 +- credentialsd-ui/src/gui/view_model/mod.rs | 2 +- credentialsd-ui/src/main.rs | 2 +- credentialsd/src/credential_service/hybrid.rs | 5 +- credentialsd/src/credential_service/mod.rs | 5 +- credentialsd/src/credential_service/nfc.rs | 5 +- credentialsd/src/credential_service/usb.rs | 11 +- credentialsd/src/dbus/flow_control.rs | 6 +- credentialsd/src/dbus/ui_control.rs | 5 +- credentialsd/src/gateway/dbus.rs | 2 +- credentialsd/src/gateway/mod.rs | 2 +- 18 files changed, 656 insertions(+), 704 deletions(-) delete mode 100644 credentialsd-common/src/server.rs diff --git a/credentialsd-common/src/lib.rs b/credentialsd-common/src/lib.rs index f6ad956..6db332d 100644 --- a/credentialsd-common/src/lib.rs +++ b/credentialsd-common/src/lib.rs @@ -1,4 +1,3 @@ pub mod client; pub mod memfd; pub mod model; -pub mod server; diff --git a/credentialsd-common/src/model.rs b/credentialsd-common/src/model.rs index 3eeea36..437697a 100644 --- a/credentialsd-common/src/model.rs +++ b/credentialsd-common/src/model.rs @@ -1,9 +1,276 @@ use std::fmt::Display; -use serde::{Deserialize, Serialize}; -use zvariant::{DeserializeDict, Optional, OwnedFd, SerializeDict, Type}; +use serde::{ + Deserialize, Serialize, + de::{DeserializeSeed, Error as _, Visitor}, +}; -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +use zvariant::{ + self, Array, DeserializeDict, DynamicDeserialize, Fd, NoneValue, Optional, OwnedFd, OwnedValue, + SerializeDict, Signature, Str, Structure, StructureBuilder, Type, Value, signature::Fields, +}; + +const TAG_VALUE_SIGNATURE: &Signature = &Signature::Structure(Fields::Static { + fields: &[&Signature::U32, &Signature::Variant], +}); + +/// Ceremony completed successfully +const BACKGROUND_EVENT_CEREMONY_COMPLETED: u32 = 0x01; +/// Device needs the client PIN to be entered. The backend should collect the +/// PIN and send it back with `EnterClientPin` event of `UserInteracted` signal. +const BACKGROUND_EVENT_NEEDS_PIN: u32 = 0x10; +const BACKGROUND_EVENT_NEEDS_USER_VERIFICATION: u32 = 0x11; +const BACKGROUND_EVENT_NEEDS_USER_PRESENCE: u32 = 0x12; +const BACKGROUND_EVENT_SELECTING_CREDENTIAL: u32 = 0x13; + +const BACKGROUND_EVENT_HYBRID_IDLE: u32 = 0x20; +const BACKGROUND_EVENT_HYBRID_STARTED: u32 = 0x21; +const BACKGROUND_EVENT_HYBRID_CONNECTING: u32 = 0x22; +const BACKGROUND_EVENT_HYBRID_CONNECTED: u32 = 0x23; + +const BACKGROUND_EVENT_NFC_IDLE: u32 = 0x30; +const BACKGROUND_EVENT_NFC_WAITING: u32 = 0x31; +const BACKGROUND_EVENT_NFC_CONNECTED: u32 = 0x32; + +const BACKGROUND_EVENT_USB_IDLE: u32 = 0x40; +const BACKGROUND_EVENT_USB_WAITING: u32 = 0x41; +const BACKGROUND_EVENT_USB_SELECTING_DEVICE: u32 = 0x42; +const BACKGROUND_EVENT_USB_CONNECTED: u32 = 0x43; + +const BACKGROUND_EVENT_ERROR_INTERNAL: u32 = 0x80000001; +const BACKGROUND_EVENT_ERROR_TIMED_OUT: u32 = 0x80000002; +const BACKGROUND_EVENT_ERROR_CANCELLED: u32 = 0x80000003; +const BACKGROUND_EVENT_ERROR_AUTHENTICATOR: u32 = 0x80000004; +const BACKGROUND_EVENT_ERROR_NO_CREDENTIALS: u32 = 0x80000005; +const BACKGROUND_EVENT_ERROR_CREDENTIAL_EXCLUDED: u32 = 0x80000006; +const BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED: u32 = 0x80000007; +const BACKGROUND_EVENT_ERROR_PIN_NOT_SET: u32 = 0x80000008; + +const USER_INTERACTED_EVENT_DISCOVERY_REQUESTED: u32 = 0x01; +const USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED: u32 = 0x04; +const USER_INTERACTED_EVENT_CREDENTIAL_SELECTED: u32 = 0x05; +const USER_INTERACTED_EVENT_REQUEST_CANCELLED: u32 = 0x06; + +/// Credential service events intended to inform the UI. +#[derive(Debug, PartialEq)] +pub enum BackgroundEvent { + CeremonyCompleted, + NeedsPin { attempts_left: Option }, + NeedsUserVerification { attempts_left: Option }, + NeedsUserPresence, + SelectingCredential { creds: Vec }, + + HybridIdle, + HybridStarted(OwnedFd), + HybridConnecting, + HybridConnected, + + NfcIdle, + NfcWaiting, + NfcConnected, + + UsbIdle, + UsbWaiting, + UsbSelectingDevice, + UsbConnected, + + ErrorInternal, + ErrorTimedOut, + ErrorCancelled, + ErrorAuthenticator, + ErrorNoCredentials, + ErrorCredentialExcluded, + ErrorPinAttemptsExhausted, + ErrorPinNotSet, +} + +impl BackgroundEvent { + fn tag(&self) -> u32 { + match self { + Self::CeremonyCompleted => BACKGROUND_EVENT_CEREMONY_COMPLETED, + Self::NeedsPin { .. } => BACKGROUND_EVENT_NEEDS_PIN, + Self::NeedsUserVerification { .. } => BACKGROUND_EVENT_NEEDS_USER_VERIFICATION, + Self::NeedsUserPresence => BACKGROUND_EVENT_NEEDS_USER_PRESENCE, + Self::SelectingCredential { .. } => BACKGROUND_EVENT_SELECTING_CREDENTIAL, + + Self::HybridIdle => BACKGROUND_EVENT_HYBRID_IDLE, + Self::HybridStarted(_) => BACKGROUND_EVENT_HYBRID_STARTED, + Self::HybridConnecting => BACKGROUND_EVENT_HYBRID_CONNECTING, + Self::HybridConnected => BACKGROUND_EVENT_HYBRID_CONNECTED, + + Self::NfcIdle => BACKGROUND_EVENT_NFC_IDLE, + Self::NfcWaiting => BACKGROUND_EVENT_NFC_WAITING, + Self::NfcConnected => BACKGROUND_EVENT_NFC_CONNECTED, + + Self::UsbIdle => BACKGROUND_EVENT_USB_IDLE, + Self::UsbWaiting => BACKGROUND_EVENT_USB_WAITING, + Self::UsbSelectingDevice => BACKGROUND_EVENT_USB_SELECTING_DEVICE, + Self::UsbConnected => BACKGROUND_EVENT_USB_CONNECTED, + + Self::ErrorInternal => BACKGROUND_EVENT_ERROR_INTERNAL, + Self::ErrorTimedOut => BACKGROUND_EVENT_ERROR_TIMED_OUT, + Self::ErrorCancelled => BACKGROUND_EVENT_ERROR_CANCELLED, + Self::ErrorAuthenticator => BACKGROUND_EVENT_ERROR_AUTHENTICATOR, + Self::ErrorNoCredentials => BACKGROUND_EVENT_ERROR_NO_CREDENTIALS, + Self::ErrorCredentialExcluded => BACKGROUND_EVENT_ERROR_CREDENTIAL_EXCLUDED, + Self::ErrorPinAttemptsExhausted => BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED, + Self::ErrorPinNotSet => BACKGROUND_EVENT_ERROR_PIN_NOT_SET, + } + } +} + +impl Type for BackgroundEvent { + const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE; +} + +impl From<&BackgroundEvent> for Structure<'_> { + fn from(value: &BackgroundEvent) -> Self { + let tag = value.tag(); + let payload = match value { + // States with payloads + BackgroundEvent::NeedsPin { attempts_left } => { + Some(Value::U32(attempts_left.map(u32::from).unwrap_or(u32::MAX))) + } + BackgroundEvent::NeedsUserVerification { attempts_left } => { + Some(Value::U32(attempts_left.map(u32::from).unwrap_or(u32::MAX))) + } + BackgroundEvent::SelectingCredential { creds } => Some(Value::Array(creds.into())), + BackgroundEvent::HybridStarted(qr_data_fd) => Some(Value::Fd(qr_data_fd.into())), + // Empty + BackgroundEvent::CeremonyCompleted => None, + BackgroundEvent::NeedsUserPresence => None, + BackgroundEvent::HybridIdle => None, + BackgroundEvent::HybridConnecting => None, + BackgroundEvent::HybridConnected => None, + BackgroundEvent::NfcIdle => None, + BackgroundEvent::NfcWaiting => None, + BackgroundEvent::NfcConnected => None, + BackgroundEvent::UsbIdle => None, + BackgroundEvent::UsbWaiting => None, + BackgroundEvent::UsbSelectingDevice => None, + BackgroundEvent::UsbConnected => None, + BackgroundEvent::ErrorInternal => None, + BackgroundEvent::ErrorTimedOut => None, + BackgroundEvent::ErrorCancelled => None, + BackgroundEvent::ErrorAuthenticator => None, + BackgroundEvent::ErrorNoCredentials => None, + BackgroundEvent::ErrorCredentialExcluded => None, + BackgroundEvent::ErrorPinAttemptsExhausted => None, + BackgroundEvent::ErrorPinNotSet => None, + }; + tag_value_to_struct(tag, payload) + } +} + +impl TryFrom<&Structure<'_>> for BackgroundEvent { + type Error = zvariant::Error; + + fn try_from(value: &Structure<'_>) -> Result { + let (tag, value) = parse_tag_value_struct(value)?; + + match tag { + BACKGROUND_EVENT_CEREMONY_COMPLETED => Ok(Self::CeremonyCompleted), + BACKGROUND_EVENT_NEEDS_PIN => value.downcast::().map(|attempts_left| { + if attempts_left == u32::MAX { + Self::NeedsPin { + attempts_left: None, + } + } else { + Self::NeedsPin { + attempts_left: Some(attempts_left), + } + } + }), + BACKGROUND_EVENT_NEEDS_USER_VERIFICATION => { + value.downcast::().map(|attempts_left| { + if attempts_left == u32::MAX { + Self::NeedsUserVerification { + attempts_left: None, + } + } else { + Self::NeedsUserVerification { + attempts_left: Some(attempts_left), + } + } + }) + } + BACKGROUND_EVENT_NEEDS_USER_PRESENCE => Ok(Self::NeedsUserPresence), + BACKGROUND_EVENT_SELECTING_CREDENTIAL => { + let creds: Array = value.downcast_ref()?; + let creds: Result, zvariant::Error> = creds + .iter() + .map(|v| v.try_to_owned().unwrap()) + .map(|v| { + let cred: Result = Value::from(v) + .downcast::() + .map(Credential::from); + cred + }) + .collect(); + Ok(Self::SelectingCredential { creds: creds? }) + } + + BACKGROUND_EVENT_HYBRID_IDLE => Ok(Self::HybridIdle), + BACKGROUND_EVENT_HYBRID_STARTED => { + let qr_data_fd = value.downcast_ref::()?.try_to_owned()?; + Ok(Self::HybridStarted(qr_data_fd.into())) + } + BACKGROUND_EVENT_HYBRID_CONNECTING => Ok(Self::HybridConnecting), + BACKGROUND_EVENT_HYBRID_CONNECTED => Ok(Self::HybridConnected), + + BACKGROUND_EVENT_NFC_IDLE => Ok(Self::NfcIdle), + BACKGROUND_EVENT_NFC_WAITING => Ok(Self::NfcWaiting), + BACKGROUND_EVENT_NFC_CONNECTED => Ok(Self::NfcConnected), + + BACKGROUND_EVENT_USB_IDLE => Ok(Self::UsbIdle), + BACKGROUND_EVENT_USB_WAITING => Ok(Self::UsbWaiting), + BACKGROUND_EVENT_USB_SELECTING_DEVICE => Ok(Self::UsbSelectingDevice), + BACKGROUND_EVENT_USB_CONNECTED => Ok(Self::UsbConnected), + + BACKGROUND_EVENT_ERROR_AUTHENTICATOR => Ok(Self::ErrorAuthenticator), + BACKGROUND_EVENT_ERROR_NO_CREDENTIALS => Ok(Self::ErrorNoCredentials), + BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED => Ok(Self::ErrorPinAttemptsExhausted), + BACKGROUND_EVENT_ERROR_INTERNAL => Ok(Self::ErrorInternal), + BACKGROUND_EVENT_ERROR_TIMED_OUT => Ok(Self::ErrorTimedOut), + BACKGROUND_EVENT_ERROR_CANCELLED => Ok(Self::ErrorCancelled), + _ => Err(zvariant::Error::Message(format!( + "Unknown BackgroundEvent tag : {tag}" + ))), + } + } +} + +impl Serialize for BackgroundEvent { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let structure: Structure = self.into(); + structure.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for BackgroundEvent { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| { + D::Error::custom(format!( + "could not create deserializer for tag-value struct: {err}" + )) + })?; + let structure = d.deserialize(deserializer)?; + (&structure).try_into().map_err(|err| { + D::Error::custom(format!( + "could not deserialize structure into BackgroundEvent: {err}" + )) + }) + } +} + +#[derive(Clone, Debug, Default, SerializeDict, DeserializeDict, PartialEq, Type, Value)] +#[zvariant(signature = "dict")] pub struct Credential { pub id: String, pub name: String, @@ -16,6 +283,59 @@ pub struct Device { pub transport: Transport, } +#[derive(Debug, Clone)] +pub enum Error { + /// Some unknown error with the authenticator occurred. + AuthenticatorError, + /// No matching credentials were found on the device. + NoCredentials, + /// Credential was already registered with this device (credential ID contained in excludeCredentials) + CredentialExcluded, + /// Too many incorrect PIN attempts, and authenticator must be removed and + /// reinserted to continue any more PIN attempts. + /// + /// Note that this is different than exhausting the PIN count that fully + /// locks out the device. + PinAttemptsExhausted, + /// The RP requires user verification, but the device has no PIN/Biometrics set. + PinNotSet, + // TODO: We may want to hide the details on this variant from the public API. + /// Something went wrong with the credential service itself, not the authenticator. + Internal(String), +} + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AuthenticatorError => f.write_str("AuthenticatorError"), + Self::PinNotSet => f.write_str("PinNotSet"), + Self::NoCredentials => f.write_str("NoCredentials"), + Self::CredentialExcluded => f.write_str("CredentialExcluded"), + Self::PinAttemptsExhausted => f.write_str("PinAttemptsExhausted"), + Self::Internal(s) => write!(f, "InternalError: {s}"), + } + } +} + +impl TryFrom<&Value<'_>> for Error { + type Error = zvariant::Error; + + fn try_from(value: &Value<'_>) -> Result { + let err_code: &str = value.downcast_ref()?; + let err = match err_code { + "AuthenticatorError" => crate::model::Error::AuthenticatorError, + "PinNotSet" => crate::model::Error::PinNotSet, + "NoCredentials" => crate::model::Error::NoCredentials, + "CredentialExcluded" => crate::model::Error::CredentialExcluded, + "PinAttemptsExhausted" => crate::model::Error::PinAttemptsExhausted, + s => crate::model::Error::Internal(String::from(s)), + }; + Ok(err) + } +} + #[derive(Clone, Debug, Serialize, Deserialize, Type)] pub enum Operation { PublicKeyCreate, @@ -124,38 +444,314 @@ impl std::fmt::Debug for UserInteractedEvent { } } -#[derive(Debug, Clone)] -pub enum Error { - /// Some unknown error with the authenticator occurred. - AuthenticatorError, - /// No matching credentials were found on the device. - NoCredentials, - /// Credential was already registered with this device (credential ID contained in excludeCredentials) - CredentialExcluded, - /// Too many incorrect PIN attempts, and authenticator must be removed and - /// reinserted to continue any more PIN attempts. - /// - /// Note that this is different than exhausting the PIN count that fully - /// locks out the device. - PinAttemptsExhausted, - /// The RP requires user verification, but the device has no PIN/Biometrics set. - PinNotSet, - // TODO: We may want to hide the details on this variant from the public API. - /// Something went wrong with the credential service itself, not the authenticator. - Internal(String), +impl Type for UserInteractedEvent { + const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE; } -impl std::error::Error for Error {} +impl From<&UserInteractedEvent> for Structure<'_> { + fn from(value: &UserInteractedEvent) -> Self { + match value { + UserInteractedEvent::DiscoveryRequested => { + tag_value_to_struct(USER_INTERACTED_EVENT_DISCOVERY_REQUESTED, None) + } + UserInteractedEvent::ClientPinEntered(pin_fd) => tag_value_to_struct( + USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED, + Some(Value::Fd(pin_fd.into())), + ), + UserInteractedEvent::CredentialSelected(credential_id) => tag_value_to_struct( + USER_INTERACTED_EVENT_CREDENTIAL_SELECTED, + Some(Value::Str(credential_id.into())), + ), + UserInteractedEvent::RequestCancelled => { + tag_value_to_struct(USER_INTERACTED_EVENT_REQUEST_CANCELLED, None) + } + } + } +} -impl Display for Error { +impl TryFrom<&Structure<'_>> for UserInteractedEvent { + type Error = zvariant::Error; + + fn try_from(value: &Structure<'_>) -> Result { + let (tag, value) = parse_tag_value_struct(value)?; + + match tag { + USER_INTERACTED_EVENT_DISCOVERY_REQUESTED => { + Ok(UserInteractedEvent::DiscoveryRequested) + } + USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED => { + let fd = value.downcast_ref::()?; + let owned_fd = fd.try_to_owned()?; + Ok(UserInteractedEvent::ClientPinEntered(owned_fd.into())) + } + USER_INTERACTED_EVENT_CREDENTIAL_SELECTED => { + let s: Str = value.downcast_ref()?; + if s.is_empty() { + return Err(zvariant::Error::invalid_length( + s.len(), + &"a non-empty string", + )); + } + Ok(UserInteractedEvent::CredentialSelected( + s.as_str().to_string(), + )) + } + USER_INTERACTED_EVENT_REQUEST_CANCELLED => Ok(UserInteractedEvent::RequestCancelled), + _ => Err(zvariant::Error::Message(format!( + "Unknown {} tag : {tag}", + stringify!(UserInteractedEvent) + ))), + } + } +} + +impl Serialize for UserInteractedEvent { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let structure: Structure = self.into(); + structure.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for UserInteractedEvent { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| { + D::Error::custom(format!( + "could not create deserializer for tag-value struct: {err}" + )) + })?; + let structure = d.deserialize(deserializer)?; + (&structure).try_into().map_err(|err| { + D::Error::custom(format!( + "could not deserialize structure into {}: {err}", + stringify!(UserInteractedEvent) + )) + }) + } +} + +#[derive(Clone, Debug, PartialEq, Type)] +#[zvariant(signature = "s")] +pub enum WindowHandle { + Wayland(String), + X11(String), +} + +impl NoneValue for WindowHandle { + type NoneType = String; + + fn null_value() -> Self::NoneType { + String::new() + } +} + +impl Serialize for WindowHandle { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for WindowHandle { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(WindowHandleVisitor {}) + } +} + +struct WindowHandleVisitor; + +impl<'de> Visitor<'de> for WindowHandleVisitor { + type Value = WindowHandle; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "a window handle formatted as `:`" + ) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + v.try_into().map_err(E::custom) + } +} + +impl TryFrom for WindowHandle { + type Error = String; + + fn try_from(value: String) -> Result { + WindowHandle::try_from(value.as_ref()) + } +} + +impl TryFrom<&str> for WindowHandle { + type Error = String; + + fn try_from(value: &str) -> Result { + match value.split_once(':') { + Some(("x11", handle)) => Ok(Self::X11(handle.to_string())), + Some(("wayland", xid)) => Ok(Self::Wayland(xid.to_string())), + Some((window_system, _)) => Err(format!("Unknown windowing system: {window_system}")), + None => Err("Invalid window handle string format".to_string()), + } + } +} + +impl Display for WindowHandle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::AuthenticatorError => f.write_str("AuthenticatorError"), - Self::PinNotSet => f.write_str("PinNotSet"), - Self::NoCredentials => f.write_str("NoCredentials"), - Self::CredentialExcluded => f.write_str("CredentialExcluded"), - Self::PinAttemptsExhausted => f.write_str("PinAttemptsExhausted"), - Self::Internal(s) => write!(f, "InternalError: {s}"), + Self::Wayland(handle) => write!(f, "wayland:{handle}"), + Self::X11(xid) => write!(f, "x11:{xid}"), + } + } +} + +fn value_to_owned(value: &Value<'_>) -> OwnedValue { + value + .try_to_owned() + .expect("non-file descriptor values to succeed") +} + +fn parse_tag_value_struct<'a>(s: &'a Structure) -> Result<(u32, Value<'a>), zvariant::Error> { + if s.signature() != TAG_VALUE_SIGNATURE { + return Err(zvariant::Error::SignatureMismatch( + s.signature().clone(), + TAG_VALUE_SIGNATURE.to_string(), + )); + } + let tag: u32 = s + .fields() + .first() + .ok_or_else(|| { + zvariant::Error::SignatureMismatch(Signature::U32, "expected a u32 tag".to_string()) + }) + .and_then(|f| f.downcast_ref())?; + let value = s + .fields() + .get(1) + .ok_or_else(|| { + zvariant::Error::SignatureMismatch( + Signature::Variant, + "expected a variant value".to_string(), + ) + })? + .clone(); + Ok((tag, value)) +} + +fn tag_value_to_struct(tag: u32, value: Option>) -> Structure<'static> { + StructureBuilder::new() + .add_field(tag) + .append_field(Value::new(value_to_owned( + &value.unwrap_or_else(|| Value::U8(0)), + ))) + .build() + .expect("create a struct") +} + +#[cfg(test)] +mod test { + use std::os::fd::{FromRawFd, OwnedFd}; + + use zvariant::Type; + + use super::{BackgroundEvent, Credential}; + + #[test] + fn test_round_trip_completed_event() { + let event1 = BackgroundEvent::CeremonyCompleted; + let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); + let data = zvariant::to_bytes(ctx, &event1).unwrap(); + assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); + assert_eq!(&[0, 0, 0, 1, 1, b'y', 0, 0], data.bytes()); + let event2 = data.deserialize().unwrap().0; + assert_eq!(event1, event2); + } + + #[test] + fn test_round_trip_background_hybrid_event() { + let mut fds = [0; 2]; + unsafe { + libc::pipe(fds.as_mut_ptr()); } + // Wrap the raw fds into safe OwnedFd instances + let mock_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) }; + let _mock_fd2 = unsafe { OwnedFd::from_raw_fd(fds[1]) }; + + println!("mock_fd: {mock_fd:?}"); + let event1 = BackgroundEvent::HybridStarted(mock_fd.into()); + let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); + assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); + let data = zvariant::to_bytes(ctx, &event1).unwrap(); + println!("data: {data:?}"); + // handle value is 0_u32 because it's the index into the list of fds in the message. Since + // there's only one, it will be 0. + let expected = b"\x00\x00\x00\x21\x01h\0\0\0\0\0\0"; + assert_eq!(expected, data.bytes()); + let event2 = data.deserialize().unwrap().0; + // I believe that the fd is `dup()`'d through the serialization/deserialization process, so + // we can't compare the numbers for equality. + assert!(matches!(event2, BackgroundEvent::HybridStarted(_))); + } + + #[test] + fn test_round_trip_selecting_credential_state() { + let creds = vec![ + Credential { + id: "a1b2c3".to_string(), + name: "user 1".to_string(), + username: Some("u1@example.com".to_string()), + }, + Credential { + id: "321".to_string(), + name: "User 2".to_string(), + username: None, + }, + ]; + let event1 = BackgroundEvent::SelectingCredential { creds }; + let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); + let data = zvariant::to_bytes(ctx, &event1).unwrap(); + assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); + + #[rustfmt::skip] + let expected = [ + 0, 0, 0, 0x13, // BACKGROUND_EVENT_SELECTING_CREDENTIAL + 6, b'a', b'a', b'{', b's', b'v', b'}', 0, // Signature aa{sv} + padding(1) + 0, 0, 0, 143, // array(struct) data length + 0, 0, 0, 83, 0, 0, 0, 0, // element 1(struct) length, + padding(4) + 0, 0, 0, 2, 105, 100, 0, // string[2] "id" + 1, 115, 0, 0, 0, // Signature s + padding + 0, 0, 0, 6, 97, 49, 98, 50, 99, 51, 0, 0, // String, len 6, "a1b2c3" + padding(1) + 0, 0, 0, 4, 110, 97, 109, 101, 0, // String, len 4, "name" + 1, 115, 0, // Signature s + padding + 0, 0, 0, 6, 117, 115, 101, 114, 32, 49, 0, 0, // String, len 6, "user 1" + padding(1) + 0, 0, 0, 8, 117, 115, 101, 114, 110, 97, 109, 101, 0, // String, len 8, "username" + 1, 115, 0, // Signature s + 0, 0, 0, 14, 117, 49, 64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 0, 0, // String, len 14, "u1@example.com" + padding(1) + + 0, 0, 0, 47, // element 2, length 69 + 0, 0, 0, 2, 105, 100, 0, // string, len 2, "id" + 1, 115, 0, 0, 0, // Signature s + padding(2) + 0, 0, 0, 3, 51, 50, 49, 0, 0, 0, 0, 0, // string, len 3, "321" + padding(4) + 0, 0, 0, 4, 110, 97, 109, 101, 0, // String, len 4, "name" + 1, 115, 0, // Signature s + 0, 0, 0, 6, 85, 115, 101, 114, 32, 50, 0, // String, len 6, "User 2" + padding(1) + // username omitted + ]; + assert_eq!(expected, data.bytes()); + let event2: BackgroundEvent = data.deserialize().unwrap().0; + assert_eq!(event1, event2); } } diff --git a/credentialsd-common/src/server.rs b/credentialsd-common/src/server.rs deleted file mode 100644 index 06d6b3f..0000000 --- a/credentialsd-common/src/server.rs +++ /dev/null @@ -1,642 +0,0 @@ -//! Types for serializing across D-Bus instances - -use std::fmt::Display; - -use serde::{ - Deserialize, Serialize, - de::{DeserializeSeed, Error, Visitor}, -}; -use zvariant::{ - self, Array, DeserializeDict, DynamicDeserialize, Fd, NoneValue, OwnedFd, OwnedValue, - SerializeDict, Signature, Str, Structure, StructureBuilder, Type, Value, signature::Fields, -}; - -use crate::model::UserInteractedEvent; - -const TAG_VALUE_SIGNATURE: &Signature = &Signature::Structure(Fields::Static { - fields: &[&Signature::U32, &Signature::Variant], -}); - -/// Ceremony completed successfully -const BACKGROUND_EVENT_CEREMONY_COMPLETED: u32 = 0x01; -/// Device needs the client PIN to be entered. The backend should collect the -/// PIN and send it back with `EnterClientPin` event of `UserInteracted` signal. -const BACKGROUND_EVENT_NEEDS_PIN: u32 = 0x10; -const BACKGROUND_EVENT_NEEDS_USER_VERIFICATION: u32 = 0x11; -const BACKGROUND_EVENT_NEEDS_USER_PRESENCE: u32 = 0x12; -const BACKGROUND_EVENT_SELECTING_CREDENTIAL: u32 = 0x13; - -const BACKGROUND_EVENT_HYBRID_IDLE: u32 = 0x20; -const BACKGROUND_EVENT_HYBRID_STARTED: u32 = 0x21; -const BACKGROUND_EVENT_HYBRID_CONNECTING: u32 = 0x22; -const BACKGROUND_EVENT_HYBRID_CONNECTED: u32 = 0x23; - -const BACKGROUND_EVENT_NFC_IDLE: u32 = 0x30; -const BACKGROUND_EVENT_NFC_WAITING: u32 = 0x31; -const BACKGROUND_EVENT_NFC_CONNECTED: u32 = 0x32; - -const BACKGROUND_EVENT_USB_IDLE: u32 = 0x40; -const BACKGROUND_EVENT_USB_WAITING: u32 = 0x41; -const BACKGROUND_EVENT_USB_SELECTING_DEVICE: u32 = 0x42; -const BACKGROUND_EVENT_USB_CONNECTED: u32 = 0x43; - -const BACKGROUND_EVENT_ERROR_INTERNAL: u32 = 0x80000001; -const BACKGROUND_EVENT_ERROR_TIMED_OUT: u32 = 0x80000002; -const BACKGROUND_EVENT_ERROR_CANCELLED: u32 = 0x80000003; -const BACKGROUND_EVENT_ERROR_AUTHENTICATOR: u32 = 0x80000004; -const BACKGROUND_EVENT_ERROR_NO_CREDENTIALS: u32 = 0x80000005; -const BACKGROUND_EVENT_ERROR_CREDENTIAL_EXCLUDED: u32 = 0x80000006; -const BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED: u32 = 0x80000007; -const BACKGROUND_EVENT_ERROR_PIN_NOT_SET: u32 = 0x80000008; - -const USER_INTERACTED_EVENT_DISCOVERY_REQUESTED: u32 = 0x01; -const USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED: u32 = 0x04; -const USER_INTERACTED_EVENT_CREDENTIAL_SELECTED: u32 = 0x05; -const USER_INTERACTED_EVENT_REQUEST_CANCELLED: u32 = 0x06; - -/// Flattened enum BackgroundEvent for sending across D-Bus. -#[derive(Debug, PartialEq)] -pub enum BackgroundEvent { - CeremonyCompleted, - NeedsPin { attempts_left: Option }, - NeedsUserVerification { attempts_left: Option }, - NeedsUserPresence, - SelectingCredential { creds: Vec }, - - HybridIdle, - HybridStarted(OwnedFd), - HybridConnecting, - HybridConnected, - - NfcIdle, - NfcWaiting, - NfcConnected, - - UsbIdle, - UsbWaiting, - UsbSelectingDevice, - UsbConnected, - - ErrorInternal, - ErrorTimedOut, - ErrorCancelled, - ErrorAuthenticator, - ErrorNoCredentials, - ErrorCredentialExcluded, - ErrorPinAttemptsExhausted, - ErrorPinNotSet, -} - -impl BackgroundEvent { - fn tag(&self) -> u32 { - match self { - Self::CeremonyCompleted => BACKGROUND_EVENT_CEREMONY_COMPLETED, - Self::NeedsPin { .. } => BACKGROUND_EVENT_NEEDS_PIN, - Self::NeedsUserVerification { .. } => BACKGROUND_EVENT_NEEDS_USER_VERIFICATION, - Self::NeedsUserPresence => BACKGROUND_EVENT_NEEDS_USER_PRESENCE, - Self::SelectingCredential { .. } => BACKGROUND_EVENT_SELECTING_CREDENTIAL, - - Self::HybridIdle => BACKGROUND_EVENT_HYBRID_IDLE, - Self::HybridStarted(_) => BACKGROUND_EVENT_HYBRID_STARTED, - Self::HybridConnecting => BACKGROUND_EVENT_HYBRID_CONNECTING, - Self::HybridConnected => BACKGROUND_EVENT_HYBRID_CONNECTED, - - Self::NfcIdle => BACKGROUND_EVENT_NFC_IDLE, - Self::NfcWaiting => BACKGROUND_EVENT_NFC_WAITING, - Self::NfcConnected => BACKGROUND_EVENT_NFC_CONNECTED, - - Self::UsbIdle => BACKGROUND_EVENT_USB_IDLE, - Self::UsbWaiting => BACKGROUND_EVENT_USB_WAITING, - Self::UsbSelectingDevice => BACKGROUND_EVENT_USB_SELECTING_DEVICE, - Self::UsbConnected => BACKGROUND_EVENT_USB_CONNECTED, - - Self::ErrorInternal => BACKGROUND_EVENT_ERROR_INTERNAL, - Self::ErrorTimedOut => BACKGROUND_EVENT_ERROR_TIMED_OUT, - Self::ErrorCancelled => BACKGROUND_EVENT_ERROR_CANCELLED, - Self::ErrorAuthenticator => BACKGROUND_EVENT_ERROR_AUTHENTICATOR, - Self::ErrorNoCredentials => BACKGROUND_EVENT_ERROR_NO_CREDENTIALS, - Self::ErrorCredentialExcluded => BACKGROUND_EVENT_ERROR_CREDENTIAL_EXCLUDED, - Self::ErrorPinAttemptsExhausted => BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED, - Self::ErrorPinNotSet => BACKGROUND_EVENT_ERROR_PIN_NOT_SET, - } - } -} - -impl Type for BackgroundEvent { - const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE; -} - -impl From<&BackgroundEvent> for Structure<'_> { - fn from(value: &BackgroundEvent) -> Self { - let tag = value.tag(); - let payload = match value { - // States with payloads - BackgroundEvent::NeedsPin { attempts_left } => { - Some(Value::U32(attempts_left.map(u32::from).unwrap_or(u32::MAX))) - } - BackgroundEvent::NeedsUserVerification { attempts_left } => { - Some(Value::U32(attempts_left.map(u32::from).unwrap_or(u32::MAX))) - } - BackgroundEvent::SelectingCredential { creds } => Some(Value::Array(creds.into())), - BackgroundEvent::HybridStarted(qr_data_fd) => Some(Value::Fd(qr_data_fd.into())), - // Empty - BackgroundEvent::CeremonyCompleted => None, - BackgroundEvent::NeedsUserPresence => None, - BackgroundEvent::HybridIdle => None, - BackgroundEvent::HybridConnecting => None, - BackgroundEvent::HybridConnected => None, - BackgroundEvent::NfcIdle => None, - BackgroundEvent::NfcWaiting => None, - BackgroundEvent::NfcConnected => None, - BackgroundEvent::UsbIdle => None, - BackgroundEvent::UsbWaiting => None, - BackgroundEvent::UsbSelectingDevice => None, - BackgroundEvent::UsbConnected => None, - BackgroundEvent::ErrorInternal => None, - BackgroundEvent::ErrorTimedOut => None, - BackgroundEvent::ErrorCancelled => None, - BackgroundEvent::ErrorAuthenticator => None, - BackgroundEvent::ErrorNoCredentials => None, - BackgroundEvent::ErrorCredentialExcluded => None, - BackgroundEvent::ErrorPinAttemptsExhausted => None, - BackgroundEvent::ErrorPinNotSet => None, - }; - tag_value_to_struct(tag, payload) - } -} - -impl TryFrom<&Structure<'_>> for BackgroundEvent { - type Error = zvariant::Error; - - fn try_from(value: &Structure<'_>) -> Result { - let (tag, value) = parse_tag_value_struct(value)?; - - match tag { - BACKGROUND_EVENT_CEREMONY_COMPLETED => Ok(Self::CeremonyCompleted), - BACKGROUND_EVENT_NEEDS_PIN => value.downcast::().map(|attempts_left| { - if attempts_left == u32::MAX { - Self::NeedsPin { - attempts_left: None, - } - } else { - Self::NeedsPin { - attempts_left: Some(attempts_left), - } - } - }), - BACKGROUND_EVENT_NEEDS_USER_VERIFICATION => { - value.downcast::().map(|attempts_left| { - if attempts_left == u32::MAX { - Self::NeedsUserVerification { - attempts_left: None, - } - } else { - Self::NeedsUserVerification { - attempts_left: Some(attempts_left), - } - } - }) - } - BACKGROUND_EVENT_NEEDS_USER_PRESENCE => Ok(Self::NeedsUserPresence), - BACKGROUND_EVENT_SELECTING_CREDENTIAL => { - let creds: Array = value.downcast_ref()?; - let creds: Result, zvariant::Error> = creds - .iter() - .map(|v| v.try_to_owned().unwrap()) - .map(|v| { - let cred: Result = Value::from(v) - .downcast::() - .map(Credential::from); - cred - }) - .collect(); - Ok(Self::SelectingCredential { creds: creds? }) - } - - BACKGROUND_EVENT_HYBRID_IDLE => Ok(Self::HybridIdle), - BACKGROUND_EVENT_HYBRID_STARTED => { - let qr_data_fd = value.downcast_ref::()?.try_to_owned()?; - Ok(Self::HybridStarted(qr_data_fd.into())) - } - BACKGROUND_EVENT_HYBRID_CONNECTING => Ok(Self::HybridConnecting), - BACKGROUND_EVENT_HYBRID_CONNECTED => Ok(Self::HybridConnected), - - BACKGROUND_EVENT_NFC_IDLE => Ok(Self::NfcIdle), - BACKGROUND_EVENT_NFC_WAITING => Ok(Self::NfcWaiting), - BACKGROUND_EVENT_NFC_CONNECTED => Ok(Self::NfcConnected), - - BACKGROUND_EVENT_USB_IDLE => Ok(Self::UsbIdle), - BACKGROUND_EVENT_USB_WAITING => Ok(Self::UsbWaiting), - BACKGROUND_EVENT_USB_SELECTING_DEVICE => Ok(Self::UsbSelectingDevice), - BACKGROUND_EVENT_USB_CONNECTED => Ok(Self::UsbConnected), - - BACKGROUND_EVENT_ERROR_AUTHENTICATOR => Ok(Self::ErrorAuthenticator), - BACKGROUND_EVENT_ERROR_NO_CREDENTIALS => Ok(Self::ErrorNoCredentials), - BACKGROUND_EVENT_ERROR_PIN_ATTEMPTS_EXHAUSTED => Ok(Self::ErrorPinAttemptsExhausted), - BACKGROUND_EVENT_ERROR_INTERNAL => Ok(Self::ErrorInternal), - BACKGROUND_EVENT_ERROR_TIMED_OUT => Ok(Self::ErrorTimedOut), - BACKGROUND_EVENT_ERROR_CANCELLED => Ok(Self::ErrorCancelled), - _ => Err(zvariant::Error::Message(format!( - "Unknown BackgroundEvent tag : {tag}" - ))), - } - } -} - -impl Serialize for BackgroundEvent { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let structure: Structure = self.into(); - structure.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for BackgroundEvent { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| { - D::Error::custom(format!( - "could not create deserializer for tag-value struct: {err}" - )) - })?; - let structure = d.deserialize(deserializer)?; - (&structure).try_into().map_err(|err| { - D::Error::custom(format!( - "could not deserialize structure into BackgroundEvent: {err}" - )) - }) - } -} - -#[derive(Debug, Clone, SerializeDict, DeserializeDict, PartialEq, Type, Value)] -#[zvariant(signature = "dict")] -pub struct Credential { - pub id: String, - pub name: String, - pub username: Option, -} - -impl From<&Credential> for crate::model::Credential { - fn from(value: &Credential) -> Self { - Self { - id: value.id.clone(), - name: value.name.clone(), - username: value.username.clone().into(), - } - } -} - -impl From for crate::model::Credential { - fn from(value: Credential) -> Self { - Self::from(&value) - } -} - -impl From<&crate::model::Credential> for Credential { - fn from(value: &crate::model::Credential) -> Self { - Self { - id: value.id.clone(), - name: value.name.clone(), - username: value.username.clone().into(), - } - } -} - -impl From for Credential { - fn from(value: crate::model::Credential) -> Self { - Self::from(&value) - } -} - -impl TryFrom<&Value<'_>> for crate::model::Error { - type Error = zvariant::Error; - - fn try_from(value: &Value<'_>) -> Result { - let err_code: &str = value.downcast_ref()?; - let err = match err_code { - "AuthenticatorError" => crate::model::Error::AuthenticatorError, - "PinNotSet" => crate::model::Error::PinNotSet, - "NoCredentials" => crate::model::Error::NoCredentials, - "CredentialExcluded" => crate::model::Error::CredentialExcluded, - "PinAttemptsExhausted" => crate::model::Error::PinAttemptsExhausted, - s => crate::model::Error::Internal(String::from(s)), - }; - Ok(err) - } -} - -impl Type for UserInteractedEvent { - const SIGNATURE: &'static Signature = TAG_VALUE_SIGNATURE; -} - -impl From<&UserInteractedEvent> for Structure<'_> { - fn from(value: &UserInteractedEvent) -> Self { - match value { - UserInteractedEvent::DiscoveryRequested => { - tag_value_to_struct(USER_INTERACTED_EVENT_DISCOVERY_REQUESTED, None) - } - UserInteractedEvent::ClientPinEntered(pin_fd) => tag_value_to_struct( - USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED, - Some(Value::Fd(pin_fd.into())), - ), - UserInteractedEvent::CredentialSelected(credential_id) => tag_value_to_struct( - USER_INTERACTED_EVENT_CREDENTIAL_SELECTED, - Some(Value::Str(credential_id.into())), - ), - UserInteractedEvent::RequestCancelled => { - tag_value_to_struct(USER_INTERACTED_EVENT_REQUEST_CANCELLED, None) - } - } - } -} - -impl TryFrom<&Structure<'_>> for UserInteractedEvent { - type Error = zvariant::Error; - - fn try_from(value: &Structure<'_>) -> Result { - let (tag, value) = parse_tag_value_struct(value)?; - - match tag { - USER_INTERACTED_EVENT_DISCOVERY_REQUESTED => { - Ok(UserInteractedEvent::DiscoveryRequested) - } - USER_INTERACTED_EVENT_CLIENT_PIN_ENTERED => { - let fd = value.downcast_ref::()?; - let owned_fd = fd.try_to_owned()?; - Ok(UserInteractedEvent::ClientPinEntered(owned_fd.into())) - } - USER_INTERACTED_EVENT_CREDENTIAL_SELECTED => { - let s: Str = value.downcast_ref()?; - if s.is_empty() { - return Err(zvariant::Error::invalid_length( - s.len(), - &"a non-empty string", - )); - } - Ok(UserInteractedEvent::CredentialSelected( - s.as_str().to_string(), - )) - } - USER_INTERACTED_EVENT_REQUEST_CANCELLED => Ok(UserInteractedEvent::RequestCancelled), - _ => Err(zvariant::Error::Message(format!( - "Unknown {} tag : {tag}", - stringify!(UserInteractedEvent) - ))), - } - } -} - -impl Serialize for UserInteractedEvent { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let structure: Structure = self.into(); - structure.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for UserInteractedEvent { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let d = Structure::deserializer_for_signature(TAG_VALUE_SIGNATURE).map_err(|err| { - D::Error::custom(format!( - "could not create deserializer for tag-value struct: {err}" - )) - })?; - let structure = d.deserialize(deserializer)?; - (&structure).try_into().map_err(|err| { - D::Error::custom(format!( - "could not deserialize structure into {}: {err}", - stringify!(UserInteractedEvent) - )) - }) - } -} - -#[derive(Clone, Debug, PartialEq, Type)] -#[zvariant(signature = "s")] -pub enum WindowHandle { - Wayland(String), - X11(String), -} - -impl NoneValue for WindowHandle { - type NoneType = String; - - fn null_value() -> Self::NoneType { - String::new() - } -} - -impl Serialize for WindowHandle { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for WindowHandle { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_str(WindowHandleVisitor {}) - } -} - -struct WindowHandleVisitor; - -impl<'de> Visitor<'de> for WindowHandleVisitor { - type Value = WindowHandle; - - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "a window handle formatted as `:`" - ) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: serde::de::Error, - { - v.try_into().map_err(E::custom) - } -} - -impl TryFrom for WindowHandle { - type Error = String; - - fn try_from(value: String) -> Result { - WindowHandle::try_from(value.as_ref()) - } -} - -impl TryFrom<&str> for WindowHandle { - type Error = String; - - fn try_from(value: &str) -> Result { - match value.split_once(':') { - Some(("x11", handle)) => Ok(Self::X11(handle.to_string())), - Some(("wayland", xid)) => Ok(Self::Wayland(xid.to_string())), - Some((window_system, _)) => Err(format!("Unknown windowing system: {window_system}")), - None => Err("Invalid window handle string format".to_string()), - } - } -} - -impl Display for WindowHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Wayland(handle) => write!(f, "wayland:{handle}"), - Self::X11(xid) => write!(f, "x11:{xid}"), - } - } -} - -fn value_to_owned(value: &Value<'_>) -> OwnedValue { - value - .try_to_owned() - .expect("non-file descriptor values to succeed") -} - -fn parse_tag_value_struct<'a>(s: &'a Structure) -> Result<(u32, Value<'a>), zvariant::Error> { - if s.signature() != TAG_VALUE_SIGNATURE { - return Err(zvariant::Error::SignatureMismatch( - s.signature().clone(), - TAG_VALUE_SIGNATURE.to_string(), - )); - } - let tag: u32 = s - .fields() - .first() - .ok_or_else(|| { - zvariant::Error::SignatureMismatch(Signature::U32, "expected a u32 tag".to_string()) - }) - .and_then(|f| f.downcast_ref())?; - let value = s - .fields() - .get(1) - .ok_or_else(|| { - zvariant::Error::SignatureMismatch( - Signature::Variant, - "expected a variant value".to_string(), - ) - })? - .clone(); - Ok((tag, value)) -} - -fn tag_value_to_struct(tag: u32, value: Option>) -> Structure<'static> { - StructureBuilder::new() - .add_field(tag) - .append_field(Value::new(value_to_owned( - &value.unwrap_or_else(|| Value::U8(0)), - ))) - .build() - .expect("create a struct") -} - -#[cfg(test)] -mod test { - use std::os::fd::{FromRawFd, OwnedFd}; - - use zvariant::Type; - - use super::{BackgroundEvent, Credential}; - - #[test] - fn test_round_trip_completed_event() { - let event1 = BackgroundEvent::CeremonyCompleted; - let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); - let data = zvariant::to_bytes(ctx, &event1).unwrap(); - assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); - assert_eq!(&[0, 0, 0, 1, 1, b'y', 0, 0], data.bytes()); - let event2 = data.deserialize().unwrap().0; - assert_eq!(event1, event2); - } - - #[test] - fn test_round_trip_background_hybrid_event() { - let mut fds = [0; 2]; - unsafe { - libc::pipe(fds.as_mut_ptr()); - } - // Wrap the raw fds into safe OwnedFd instances - let mock_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) }; - let _mock_fd2 = unsafe { OwnedFd::from_raw_fd(fds[1]) }; - - println!("mock_fd: {mock_fd:?}"); - let event1 = BackgroundEvent::HybridStarted(mock_fd.into()); - let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); - assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); - let data = zvariant::to_bytes(ctx, &event1).unwrap(); - println!("data: {data:?}"); - // handle value is 0_u32 because it's the index into the list of fds in the message. Since - // there's only one, it will be 0. - let expected = b"\x00\x00\x00\x21\x01h\0\0\0\0\0\0"; - assert_eq!(expected, data.bytes()); - let event2 = data.deserialize().unwrap().0; - // I believe that the fd is `dup()`'d through the serialization/deserialization process, so - // we can't compare the numbers for equality. - assert!(matches!(event2, BackgroundEvent::HybridStarted(_))); - } - - #[test] - fn test_round_trip_selecting_credential_state() { - let creds = vec![ - Credential { - id: "a1b2c3".to_string(), - name: "user 1".to_string(), - username: Some("u1@example.com".to_string()), - }, - Credential { - id: "321".to_string(), - name: "User 2".to_string(), - username: None, - }, - ]; - let event1 = BackgroundEvent::SelectingCredential { creds }; - let ctx = zvariant::serialized::Context::new_dbus(zvariant::BE, 0); - let data = zvariant::to_bytes(ctx, &event1).unwrap(); - assert_eq!("(uv)", BackgroundEvent::SIGNATURE.to_string()); - - #[rustfmt::skip] - let expected = [ - 0, 0, 0, 0x13, // BACKGROUND_EVENT_SELECTING_CREDENTIAL - 6, b'a', b'a', b'{', b's', b'v', b'}', 0, // Signature aa{sv} + padding(1) - 0, 0, 0, 143, // array(struct) data length - 0, 0, 0, 83, 0, 0, 0, 0, // element 1(struct) length, + padding(4) - 0, 0, 0, 2, 105, 100, 0, // string[2] "id" - 1, 115, 0, 0, 0, // Signature s + padding - 0, 0, 0, 6, 97, 49, 98, 50, 99, 51, 0, 0, // String, len 6, "a1b2c3" + padding(1) - 0, 0, 0, 4, 110, 97, 109, 101, 0, // String, len 4, "name" - 1, 115, 0, // Signature s + padding - 0, 0, 0, 6, 117, 115, 101, 114, 32, 49, 0, 0, // String, len 6, "user 1" + padding(1) - 0, 0, 0, 8, 117, 115, 101, 114, 110, 97, 109, 101, 0, // String, len 8, "username" - 1, 115, 0, // Signature s - 0, 0, 0, 14, 117, 49, 64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 0, 0, // String, len 14, "u1@example.com" + padding(1) - - 0, 0, 0, 47, // element 2, length 69 - 0, 0, 0, 2, 105, 100, 0, // string, len 2, "id" - 1, 115, 0, 0, 0, // Signature s + padding(2) - 0, 0, 0, 3, 51, 50, 49, 0, 0, 0, 0, 0, // string, len 3, "321" + padding(4) - 0, 0, 0, 4, 110, 97, 109, 101, 0, // String, len 4, "name" - 1, 115, 0, // Signature s - 0, 0, 0, 6, 85, 115, 101, 114, 32, 50, 0, // String, len 6, "User 2" + padding(1) - // username omitted - ]; - assert_eq!(expected, data.bytes()); - let event2: BackgroundEvent = data.deserialize().unwrap().0; - assert_eq!(event1, event2); - } -} diff --git a/credentialsd-ui/src/client.rs b/credentialsd-ui/src/client.rs index a18de29..c715dcf 100644 --- a/credentialsd-ui/src/client.rs +++ b/credentialsd-ui/src/client.rs @@ -4,7 +4,8 @@ use async_std::{ }; use credentialsd_common::{ - memfd::write_secret, model::UserInteractedEvent, server::BackgroundEvent, + memfd::write_secret, + model::{BackgroundEvent, UserInteractedEvent}, }; const CTAP_CLIENT_SECRET_MAX_LEN: usize = 63; diff --git a/credentialsd-ui/src/dbus.rs b/credentialsd-ui/src/dbus.rs index 6f31e54..de36384 100644 --- a/credentialsd-ui/src/dbus.rs +++ b/credentialsd-ui/src/dbus.rs @@ -18,9 +18,8 @@ use zbus::{ zvariant::{Optional, OwnedObjectPath}, }; -use credentialsd_common::{ - model::{Device, Operation, PortalBackendOptions, UserInteractedEvent}, - server::{BackgroundEvent, WindowHandle}, +use credentialsd_common::model::{ + BackgroundEvent, Device, Operation, PortalBackendOptions, UserInteractedEvent, WindowHandle, }; use crate::{RequestingApplication, ViewRequest, client::FlowControlClient}; diff --git a/credentialsd-ui/src/gui/mod.rs b/credentialsd-ui/src/gui/mod.rs index 8f31bc6..f716817 100644 --- a/credentialsd-ui/src/gui/mod.rs +++ b/credentialsd-ui/src/gui/mod.rs @@ -6,7 +6,7 @@ use std::{sync::Arc, thread::JoinHandle}; use async_std::{channel::Receiver, sync::Mutex as AsyncMutex}; use credentialsd_common::model::Device; -use credentialsd_common::server::{Credential, WindowHandle}; +use credentialsd_common::model::{Credential, WindowHandle}; use crate::{ViewRequest, client::FlowControlClient}; diff --git a/credentialsd-ui/src/gui/view_model/gtk/application.rs b/credentialsd-ui/src/gui/view_model/gtk/application.rs index aa0a97b..00d2fe9 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/application.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/application.rs @@ -1,5 +1,5 @@ use async_std::channel::{Receiver, Sender}; -use credentialsd_common::server::WindowHandle; +use credentialsd_common::model::WindowHandle; use tracing::{debug, info}; use gtk::prelude::*; @@ -14,7 +14,7 @@ mod imp { use crate::gui::view_model::gtk::ModelState; use super::*; - use credentialsd_common::server::WindowHandle; + use credentialsd_common::model::WindowHandle; use glib::{WeakRef, clone}; use std::{ cell::{OnceCell, RefCell}, diff --git a/credentialsd-ui/src/gui/view_model/gtk/mod.rs b/credentialsd-ui/src/gui/view_model/gtk/mod.rs index 70f2c4d..e8c9017 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/mod.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/mod.rs @@ -4,7 +4,7 @@ pub mod device; mod window; use async_std::channel::{Receiver, Sender}; -use credentialsd_common::server::WindowHandle; +use credentialsd_common::model::WindowHandle; use gettextrs::{LocaleCategory, gettext, ngettext}; use glib::clone; use gtk::gdk::Texture; diff --git a/credentialsd-ui/src/gui/view_model/mod.rs b/credentialsd-ui/src/gui/view_model/mod.rs index 816f49b..d66bbf9 100644 --- a/credentialsd-ui/src/gui/view_model/mod.rs +++ b/credentialsd-ui/src/gui/view_model/mod.rs @@ -8,7 +8,7 @@ use async_std::{ sync::Mutex as AsyncMutex, }; use credentialsd_common::memfd::read_secret; -use credentialsd_common::server::{BackgroundEvent, Credential}; +use credentialsd_common::model::{BackgroundEvent, Credential}; use gettextrs::gettext; use serde::{Deserialize, Serialize}; use tracing::{error, info}; diff --git a/credentialsd-ui/src/main.rs b/credentialsd-ui/src/main.rs index 031c8c2..4265f4f 100644 --- a/credentialsd-ui/src/main.rs +++ b/credentialsd-ui/src/main.rs @@ -6,8 +6,8 @@ mod gui; use std::error::Error; +use credentialsd_common::model::WindowHandle; use credentialsd_common::model::{Device, Operation}; -use credentialsd_common::server::WindowHandle; use crate::dbus::CredentialPortalBackend; diff --git a/credentialsd/src/credential_service/hybrid.rs b/credentialsd/src/credential_service/hybrid.rs index a5c3756..d25bd1d 100644 --- a/credentialsd/src/credential_service/hybrid.rs +++ b/credentialsd/src/credential_service/hybrid.rs @@ -16,7 +16,10 @@ use libwebauthn::transport::cable::qr_code_device::{ use libwebauthn::transport::{Channel, ChannelSettings, Device}; use libwebauthn::webauthn::{Error as WebAuthnError, WebAuthn}; -use credentialsd_common::{memfd::write_secret, model::Error, server::BackgroundEvent}; +use credentialsd_common::{ + memfd::write_secret, + model::{BackgroundEvent, Error}, +}; use crate::model::CredentialRequest; diff --git a/credentialsd/src/credential_service/mod.rs b/credentialsd/src/credential_service/mod.rs index 45b86bd..b0dce50 100644 --- a/credentialsd/src/credential_service/mod.rs +++ b/credentialsd/src/credential_service/mod.rs @@ -19,9 +19,8 @@ use libwebauthn::{ use nfc::{NfcEvent, NfcHandler, NfcState, NfcStateInternal}; use tokio::sync::oneshot; -use credentialsd_common::{ - model::{Device, Error as CredentialServiceError, Transport}, - server::BackgroundEvent, +use credentialsd_common::model::{ + BackgroundEvent, Device, Error as CredentialServiceError, Transport, }; use crate::{ diff --git a/credentialsd/src/credential_service/nfc.rs b/credentialsd/src/credential_service/nfc.rs index b514814..0d4d15d 100644 --- a/credentialsd/src/credential_service/nfc.rs +++ b/credentialsd/src/credential_service/nfc.rs @@ -14,10 +14,7 @@ use tokio::sync::broadcast; use tokio::sync::mpsc::{self, Receiver, Sender, WeakSender}; use tracing::{debug, warn}; -use credentialsd_common::{ - model::{Credential, Error}, - server::BackgroundEvent, -}; +use credentialsd_common::model::{BackgroundEvent, Credential, Error}; use crate::model::{CredentialRequest, GetAssertionResponseInternal}; diff --git a/credentialsd/src/credential_service/usb.rs b/credentialsd/src/credential_service/usb.rs index 8c6311a..7d5de16 100644 --- a/credentialsd/src/credential_service/usb.rs +++ b/credentialsd/src/credential_service/usb.rs @@ -13,14 +13,13 @@ use libwebauthn::{ webauthn::{Error as WebAuthnError, WebAuthn}, UvUpdate, }; -use tokio::sync::broadcast; -use tokio::sync::mpsc::{self, Receiver, Sender, WeakSender}; +use tokio::sync::{ + broadcast, + mpsc::{self, Receiver, Sender, WeakSender}, +}; use tracing::{debug, warn}; -use credentialsd_common::{ - model::{Credential, Error}, - server::BackgroundEvent, -}; +use credentialsd_common::model::{BackgroundEvent, Credential, Error}; use crate::model::{CredentialRequest, GetAssertionResponseInternal}; diff --git a/credentialsd/src/dbus/flow_control.rs b/credentialsd/src/dbus/flow_control.rs index ed20433..2d64356 100644 --- a/credentialsd/src/dbus/flow_control.rs +++ b/credentialsd/src/dbus/flow_control.rs @@ -8,10 +8,12 @@ use std::{ }; use async_trait::async_trait; -use credentialsd_common::server::{BackgroundEvent, WindowHandle}; use credentialsd_common::{ memfd::read_secret, - model::{Error as CredentialServiceError, PortalBackendOptions, UserInteractedEvent}, + model::{ + BackgroundEvent, Error as CredentialServiceError, PortalBackendOptions, + UserInteractedEvent, WindowHandle, + }, }; use futures_lite::{Stream, StreamExt}; use tokio::sync::mpsc::Receiver; diff --git a/credentialsd/src/dbus/ui_control.rs b/credentialsd/src/dbus/ui_control.rs index ee9c548..5908e62 100644 --- a/credentialsd/src/dbus/ui_control.rs +++ b/credentialsd/src/dbus/ui_control.rs @@ -13,9 +13,8 @@ use zbus::{ Connection, }; -use credentialsd_common::{ - model::{Device, Operation, PortalBackendOptions, UserInteractedEvent}, - server::{BackgroundEvent, WindowHandle}, +use credentialsd_common::model::{ + BackgroundEvent, Device, Operation, PortalBackendOptions, UserInteractedEvent, WindowHandle, }; /// Used by the credential service to control the UI. diff --git a/credentialsd/src/gateway/dbus.rs b/credentialsd/src/gateway/dbus.rs index 9242869..10d9109 100644 --- a/credentialsd/src/gateway/dbus.rs +++ b/credentialsd/src/gateway/dbus.rs @@ -10,7 +10,7 @@ use zbus::{ Connection, DBusError, }; -use credentialsd_common::server::WindowHandle; +use credentialsd_common::model::WindowHandle; use crate::{ gateway::{ diff --git a/credentialsd/src/gateway/mod.rs b/credentialsd/src/gateway/mod.rs index 75a90d8..ef15e6f 100644 --- a/credentialsd/src/gateway/mod.rs +++ b/credentialsd/src/gateway/mod.rs @@ -11,7 +11,7 @@ use std::{ sync::Arc, }; -use credentialsd_common::server::WindowHandle; +use credentialsd_common::model::WindowHandle; use tokio::sync::Mutex as AsyncMutex; use zbus::{ zvariant::{DeserializeDict, NoneValue, OwnedValue, SerializeDict, Type},