Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,531 changes: 612 additions & 919 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ members = ["credentialsd", "credentialsd-common", "credentialsd-ui"]
lto = true

[workspace.dependencies]
futures-lite = "2.6.0"
libc = "0.2.186"
serde = { version = "1.0.219", features = ["derive"] }
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
zbus = { version = "5.9.0", default-features = false }
zvariant = "5.6.0"
futures-lite = "=2.6.1"
libc = "=0.2.189"
serde = { version = "=1.0.229", features = ["derive"] }
tracing = "=0.1.44"
tracing-subscriber = "=0.3.23"
zbus = { version = "=5.18.0", default-features = false }
zvariant = "=5.13.1"
3 changes: 2 additions & 1 deletion credentialsd-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ futures-lite.workspace = true
libc.workspace = true
serde = { workspace = true, features = ["derive"] }
tracing.workspace = true
zeroize = "1.9.0"
zvariant.workspace = true

zeroize = "=1.9.0"
17 changes: 9 additions & 8 deletions credentialsd-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ default = ["wayland"]
wayland = ["gdk-wayland"]

[dependencies]
async-std = { version = "1.13.1", features = ["unstable"] }
credentialsd-common = { path = "../credentialsd-common" }

futures-lite.workspace = true
gettext-rs = { version = "0.7", features = ["gettext-system"] }
gtk = { version = "0.10.3", package = "gtk4", features = ["v4_14"] }
gdk-wayland = { version = "0.10.3", package = "gdk4-wayland", optional = true }
qrcode = "0.14.1"
serde.workspace = true
tracing.workspace = true
tracing-subscriber = "0.3.19"
zeroize = { version = "1.8.2" }
tracing-subscriber.workspace = true
zbus = { workspace = true, default-features = false, features = ["async-io"]}
gio-unix = "0.22.6"

async-std = { version = "=1.13.2", features = ["unstable"] }
gettext-rs = { version = "=0.7.7", features = ["gettext-system"] }
gio-unix = "=0.22.8"
gtk = { version = "=0.11.4", package = "gtk4", features = ["v4_14"] }
gdk-wayland = { version = "=0.11.4", package = "gdk4-wayland", optional = true }
qrcode = "=0.14.1"
32 changes: 16 additions & 16 deletions credentialsd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@
name = "credentialsd"
version = "0.2.0"
authors = ["Isaiah Inuwa <isaiah.inuwa@gmail.com>", "Martin Sirringhaus <martin.sirringhaus@suse.com>", "Alfie Fresta <alfie.fresta@gmail.com>"]
edition = "2021"
edition = "2024"
license = "LGPL-3.0-only"

[dependencies]
async-stream = "0.3.6"
async-trait = "0.1.89"
base64 = "0.22.1"
credentialsd-common = { path = "../credentialsd-common" }
futures = "0.3.32"

futures-lite.workspace = true
libwebauthn = { version = "0.8.0", features = ["nfc-backend-libnfc", "nfc-backend-pcsc", "reqwest-related-origins-source"] }
# 0.6.1 fails to build with non-vendored library.
# https://github.com/alexrsagen/rs-nfc1/issues/15
nfc1 = { version = "=0.6.0", default-features = false }
rand = "0.9.2"
ring = "0.17.14"
serde.workspace = true
serde_json = "1.0.140"
tokio = { version = "1.45.0", features = ["rt-multi-thread"] }
tokio-stream = "0.1.18"
tracing.workspace = true
tracing-subscriber.workspace = true
zbus = { workspace = true, default-features = false, features = ["tokio"] }

async-stream = "=0.3.6"
async-trait = "=0.1.91"
base64 = "=0.22.1"
futures = "=0.3.33"
libwebauthn = { version = "=0.10.0", features = ["nfc-backend-libnfc", "nfc-backend-pcsc", "reqwest-related-origins-source"] }
nfc1 = { version = "=0.7.1", default-features = false }
rand = "=0.10.2"
ring = "=0.17.14"
serde_json = "=1.0.151"
tokio = { version = "=1.53.1", features = ["rt-multi-thread"] }
tokio-stream = "=0.1.19"

[dev-dependencies]
gio = "0.21.0"
zbus = { version = "5.9.0", default-features = false, features = ["blocking-api", "tokio"] }
gio = "=0.22.8"
zbus = { version = "=5.18.0", default-features = false, features = ["blocking-api", "tokio"] }
20 changes: 13 additions & 7 deletions credentialsd/src/credential_service/hybrid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use libwebauthn::transport::cable::qr_code_device::{
CableQrCodeDevice, CableTransports, QrCodeOperationHint,
};
use libwebauthn::transport::{Channel, ChannelSettings, Device};
use libwebauthn::webauthn::{Error as WebAuthnError, WebAuthn};
use libwebauthn::webauthn::{WebAuthn, error::WebAuthnError};

use credentialsd_common::{
memfd::write_secret,
Expand Down Expand Up @@ -101,7 +101,10 @@ impl HybridHandler for InternalHybridHandler {
Ok(response) => break Ok(response.into()),
Err(WebAuthnError::Ctap(ctap_error)) => {
if ctap_error.is_retryable_user_error() {
tracing::debug!("Retrying credential creation operation because of CTAP error: {:?}", ctap_error);
tracing::debug!(
"Retrying credential creation operation because of CTAP error: {:?}",
ctap_error
);
continue;
} else {
tracing::error!(
Expand All @@ -125,7 +128,10 @@ impl HybridHandler for InternalHybridHandler {
Ok(response) => break Ok(response.into()),
Err(WebAuthnError::Ctap(ctap_error)) => {
if ctap_error.is_retryable_user_error() {
tracing::debug!("Retrying assertion operation because of CTAP error: {:?}", ctap_error);
tracing::debug!(
"Retrying assertion operation because of CTAP error: {:?}",
ctap_error
);
continue;
} else {
tracing::error!(
Expand Down Expand Up @@ -276,10 +282,10 @@ async fn handle_hybrid_updates(
}
},
};
if let Some(state) = new_state {
if let Err(err) = state_sender.send(state.clone()).await {
error!({ ?err, ?state }, "Failed to send hybrid update");
}
if let Some(state) = new_state
&& let Err(err) = state_sender.send(state.clone()).await
{
error!({ ?err, ?state }, "Failed to send hybrid update");
}
}
}
37 changes: 20 additions & 17 deletions credentialsd/src/credential_service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,19 +180,19 @@ impl<H: HybridHandler + Send, N: NfcHandler + Send, U: UsbHandler + Send> Manage

async fn cancel_request(&self, request_id: RequestId) {
let mut guard = self.ctx.lock().expect("Lock to be taken");
if let Some(ctx) = guard.take_if(|ctx| ctx.request_id == request_id) {
if request_id == ctx.request_id {
tracing::debug!("Cancelling request {request_id}");
// TODO: cancel sub-tasks: hybrid and USB streams.

// It's fine if the requestor is no longer listening for the response.
// TODO: create Cancelled variant
_ = ctx
.response_channel
.send(Err(CredentialServiceError::Internal(format!(
"Cancelled request {request_id}."
))));
}
if let Some(ctx) = guard.take_if(|ctx| ctx.request_id == request_id)
&& request_id == ctx.request_id
{
tracing::debug!("Cancelling request {request_id}");
// TODO: cancel sub-tasks: hybrid and USB streams.

// It's fine if the requestor is no longer listening for the response.
// TODO: create Cancelled variant
_ = ctx
.response_channel
.send(Err(CredentialServiceError::Internal(format!(
"Cancelled request {request_id}."
))));
}
}

Expand Down Expand Up @@ -391,10 +391,13 @@ impl From<UsbState> for DeviceStateUpdate {
}

fn complete_request(ctx: &Mutex<Option<RequestContext>>, response: CredentialResponse) {
if let Some(ctx) = ctx.lock().unwrap().take() {
ctx.send_response(Ok(response));
} else {
tracing::error!("Tried to consume context to respond to caller, but none was found.")
match ctx.lock().unwrap().take() {
Some(ctx) => {
ctx.send_response(Ok(response));
}
_ => {
tracing::error!("Tried to consume context to respond to caller, but none was found.")
}
}
}

Expand Down
27 changes: 19 additions & 8 deletions credentialsd/src/credential_service/nfc.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::time::Duration;

use async_stream::stream;
use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use base64::{self, Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use futures_lite::Stream;
use libwebauthn::{
UvUpdate,
ops::webauthn::GetAssertionResponse,
proto::CtapError,
transport::{nfc::device::NfcDevice, Channel, ChannelSettings, Device},
webauthn::{Error as WebAuthnError, WebAuthn},
UvUpdate,
transport::{Channel, ChannelSettings, Device, nfc::device::NfcDevice},
webauthn::{WebAuthn, error::WebAuthnError},
};
use tokio::sync::broadcast;
use tokio::sync::mpsc::{self, Receiver, Sender, WeakSender};
Expand Down Expand Up @@ -209,7 +209,10 @@ async fn handle_events(
.await
{
Err(err) => {
tracing::error!("Failed to open channel to NFC authenticator, cannot receive user verification events: {:?}", err);
tracing::error!(
"Failed to open channel to NFC authenticator, cannot receive user verification events: {:?}",
err
);
}
Ok(mut channel) => {
let signal_tx2 = signal_tx.clone().downgrade();
Expand Down Expand Up @@ -478,7 +481,10 @@ async fn handle_nfc_updates(
.send(Ok(NfcUvMessage::NeedsUserVerification { attempts_left }))
.await
{
tracing::error!("Authenticator requested user verficiation, but we cannot relay the message to credential service: {:?}", err);
tracing::error!(
"Authenticator requested user verficiation, but we cannot relay the message to credential service: {:?}",
err
);
}
}
UvUpdate::PinRequired(pin_update) => {
Expand All @@ -490,7 +496,10 @@ async fn handle_nfc_updates(
}))
.await
{
tracing::error!("Authenticator requested a PIN from the user, but we cannot relay the message to the credential service: {:?}", err);
tracing::error!(
"Authenticator requested a PIN from the user, but we cannot relay the message to the credential service: {:?}",
err
);
}
match pin_rx.recv().await {
Some(pin) => match pin_update.send_pin(&pin) {
Expand All @@ -501,7 +510,9 @@ async fn handle_nfc_updates(
}
}
UvUpdate::PresenceRequired => {
tracing::debug!("Authenticator requested user presence, but that makes no sense for NFC. Skipping");
tracing::debug!(
"Authenticator requested user presence, but that makes no sense for NFC. Skipping"
);
}
UvUpdate::PinNotSet(_) => {
tracing::error!("Authenticator requested PIN setup, which is not yet supported.");
Expand Down
31 changes: 21 additions & 10 deletions credentialsd/src/credential_service/usb.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
use std::{collections::HashMap, sync::Arc, time::Duration};

use async_stream::stream;
use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use base64::{self, Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use futures_lite::Stream;
use libwebauthn::{
UvUpdate,
ops::webauthn::GetAssertionResponse,
proto::CtapError,
transport::{
hid::{channel::HidChannelHandle, HidDevice},
Channel, ChannelSettings, Device,
hid::{HidDevice, channel::HidChannelHandle},
},
webauthn::{Error as WebAuthnError, WebAuthn},
UvUpdate,
webauthn::{WebAuthn, error::WebAuthnError},
};
use tokio::sync::{
broadcast,
Mutex as AsyncMutex, broadcast,
mpsc::{self, Receiver, Sender, WeakSender},
Mutex as AsyncMutex,
};
use tracing::{debug, warn};

Expand Down Expand Up @@ -303,7 +302,10 @@ async fn handle_events(
.await;
match channel {
Err(err) => {
tracing::error!("Failed to open channel to USB authenticator, cannot receive user verification events: {:?}", err);
tracing::error!(
"Failed to open channel to USB authenticator, cannot receive user verification events: {:?}",
err
);
}
Ok(mut channel) => {
let signal_tx2 = signal_tx.clone().downgrade();
Expand Down Expand Up @@ -590,7 +592,10 @@ async fn handle_usb_updates(
.send(Ok(UsbUvMessage::NeedsUserVerification { attempts_left }))
.await
{
tracing::error!("Authenticator requested user verficiation, but we cannot relay the message to credential service: {:?}", err);
tracing::error!(
"Authenticator requested user verficiation, but we cannot relay the message to credential service: {:?}",
err
);
}
}
UvUpdate::PinRequired(pin_update) => {
Expand All @@ -602,7 +607,10 @@ async fn handle_usb_updates(
}))
.await
{
tracing::error!("Authenticator requested a PIN from the user, but we cannot relay the message to the credential service: {:?}", err);
tracing::error!(
"Authenticator requested a PIN from the user, but we cannot relay the message to the credential service: {:?}",
err
);
}
match pin_rx.recv().await {
Some(pin) => match pin_update.send_pin(&pin) {
Expand All @@ -614,7 +622,10 @@ async fn handle_usb_updates(
}
UvUpdate::PresenceRequired => {
if let Err(err) = signal_tx.send(Ok(UsbUvMessage::NeedsUserPresence)).await {
tracing::error!("Authenticator requested user presence, but we cannot relay the message to the credential service: {:?}", err);
tracing::error!(
"Authenticator requested user presence, but we cannot relay the message to the credential service: {:?}",
err
);
}
}
UvUpdate::PinNotSet(_) => {
Expand Down
11 changes: 5 additions & 6 deletions credentialsd/src/dbus/flow_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use credentialsd_common::{
use futures_lite::{Stream, StreamExt};
use tokio::sync::mpsc::Receiver;
use tokio::sync::oneshot;
use tokio::sync::{mpsc::Sender, Mutex as AsyncMutex};
use tokio::sync::{Mutex as AsyncMutex, mpsc::Sender};
use tokio::task::AbortHandle;
use zbus::connection::Connection;
use zbus::zvariant::OwnedObjectPath;
Expand All @@ -30,7 +30,7 @@ use crate::{
model::{CredentialRequest, CredentialResponse},
};
use crate::{
credential_service::{nfc::NfcState, DeviceStateUpdate, ManageDevice},
credential_service::{DeviceStateUpdate, ManageDevice, nfc::NfcState},
model::ClientDetails,
};
use crate::{dbus::ui_control::Ceremony, gateway::WebAuthnError};
Expand Down Expand Up @@ -228,11 +228,10 @@ async fn handle<M: ManageDevice + Debug + Send + Sync + 'static, UC: UiControlle
}
});
tracing::debug!("Finished setting up request {request_id}");
let cred_response = request_rx
.await
.expect("Credential service not to drop request channel before responding.");

cred_response
request_rx
.await
.expect("Credential service not to drop request channel before responding.")
}

fn forward_background_event_stream(
Expand Down
2 changes: 1 addition & 1 deletion credentialsd/src/dbus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod ui_control;

pub use self::{
flow_control::{
start_flow_control_service, CredentialRequestController, CredentialRequestControllerClient,
CredentialRequestController, CredentialRequestControllerClient, start_flow_control_service,
},
ui_control::UiControlServiceClient,
};
Loading