Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for D-Bus virtual devices #146

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ maintenance = { status = "actively-developed" }
[features]
binding-recompile = ["bindgen"]
webdriver = ["base64", "bytes", "warp", "tokio", "serde", "serde_json"]
dbus = ["zbus", "zvariant"]

[target.'cfg(target_os = "linux")'.dependencies]
libudev = "^0.2"
Expand Down Expand Up @@ -45,15 +46,17 @@ log = "0.4"
libc = "0.2"
runloop = "0.1.0"
bitflags = "1.0"
sha2 = "^0.8.2"
tokio = { version = "0.2", optional = true, features = ["macros"] }
warp = { version = "0.2.4", optional = true }
serde = { version = "1.0", optional = true, features = ["derive"] }
serde_json = { version = "1.0", optional = true }
bytes = { version = "0.5", optional = true, features = ["serde"] }
base64 = { version = "^0.10", optional = true }
zbus = { version = "1.8", optional = true }
zvariant = { version = "2.4", optional = true }

[dev-dependencies]
sha2 = "^0.8.2"
base64 = "^0.10"
env_logger = "^0.6"
getopts = "^0.2"
Expand Down
20 changes: 14 additions & 6 deletions examples/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use authenticator::{
authenticatorservice::AuthenticatorService, statecallback::StateCallback,
authenticatorservice::AuthenticatorService, statecallback::StateCallback, AppId,
AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, StatusUpdate,
};
use getopts::Options;
Expand Down Expand Up @@ -42,6 +42,8 @@ fn main() {
opts.optflag("x", "no-u2f-usb-hid", "do not enable u2f-usb-hid platforms");
#[cfg(feature = "webdriver")]
opts.optflag("w", "webdriver", "enable WebDriver virtual bus");
#[cfg(feature = "dbus")]
opts.optflag("d", "dbus", "enable access to remote token through D-Bus");

opts.optflag("h", "help", "print this help menu").optopt(
"t",
Expand Down Expand Up @@ -74,6 +76,13 @@ fn main() {
}
}

#[cfg(feature = "dbus")]
{
if matches.opt_present("dbus") {
manager.add_dbus();
}
}

let timeout_ms = match matches.opt_get_default::<u64>("timeout", 15) {
Ok(timeout_s) => {
println!("Using {}s as the timeout", &timeout_s);
Expand All @@ -96,9 +105,8 @@ fn main() {
challenge.input(challenge_str.as_bytes());
let chall_bytes = challenge.result().to_vec();

let mut application = Sha256::default();
application.input(b"http://demo.yubico.com");
let app_bytes = application.result().to_vec();
let app_bytes = b"http://demo.yubico.com".to_vec();
let app_id: AppId = app_bytes.into();

let flags = RegisterFlags::empty();

Expand Down Expand Up @@ -131,7 +139,7 @@ fn main() {
flags,
timeout_ms,
chall_bytes.clone(),
app_bytes.clone(),
app_id.clone(),
vec![],
status_tx.clone(),
callback,
Expand Down Expand Up @@ -163,7 +171,7 @@ fn main() {
flags,
timeout_ms,
chall_bytes,
vec![app_bytes],
vec![app_id.clone()],
vec![key_handle],
status_tx,
callback,
Expand Down
18 changes: 11 additions & 7 deletions src/authenticatorservice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ impl AuthenticatorService {
/// Add any detected platform transports
pub fn add_detected_transports(&mut self) {
self.add_u2f_usb_hid_platform_transports();
#[cfg(feature = "dbus")]
self.add_dbus();
}

fn add_transport(&mut self, boxed_token: Box<dyn AuthenticatorTransport + Send>) {
Expand All @@ -95,6 +97,14 @@ impl AuthenticatorService {
}
}

#[cfg(feature = "dbus")]
pub fn add_dbus(&mut self) {
match crate::virtualdevices::dbus::VirtualManager::new() {
Ok(token) => self.add_transport(Box::new(token)),
Err(e) => error!("Could not add D-Bus transport: {}", e),
}
}

pub fn register(
&mut self,
flags: crate::RegisterFlags,
Expand All @@ -105,7 +115,7 @@ impl AuthenticatorService {
status: Sender<crate::StatusUpdate>,
callback: StateCallback<crate::Result<crate::RegisterResult>>,
) -> crate::Result<()> {
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
if challenge.len() != PARAMETER_SIZE {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}

Expand Down Expand Up @@ -167,12 +177,6 @@ impl AuthenticatorService {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}

for app_id in &app_ids {
if app_id.len() != PARAMETER_SIZE {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}
}

for key_handle in &key_handles {
if key_handle.credential.len() > 256 {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
Expand Down
6 changes: 3 additions & 3 deletions src/capi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub unsafe extern "C" fn rust_u2f_app_ids_add(
id_ptr: *const u8,
id_len: usize,
) {
(*ids).push(from_raw(id_ptr, id_len));
(*ids).push(from_raw(id_ptr, id_len).into());
}

/// # Safety
Expand Down Expand Up @@ -232,7 +232,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_register(

let flags = crate::RegisterFlags::from_bits_truncate(flags);
let challenge = from_raw(challenge_ptr, challenge_len);
let application = from_raw(application_ptr, application_len);
let application = from_raw(application_ptr, application_len).into();
let key_handles = (*khs).clone();

let (status_tx, status_rx) = channel::<crate::StatusUpdate>();
Expand Down Expand Up @@ -333,7 +333,7 @@ pub unsafe extern "C" fn rust_u2f_mgr_sign(
let mut bufs = HashMap::new();
bufs.insert(RESBUF_ID_KEYHANDLE, key_handle);
bufs.insert(RESBUF_ID_SIGNATURE, signature);
bufs.insert(RESBUF_ID_APPID, app_id);
bufs.insert(RESBUF_ID_APPID, app_id.to_u2f());
bufs.insert(RESBUF_ID_VENDOR_NAME, dev_info.vendor_name);
bufs.insert(RESBUF_ID_DEVICE_NAME, dev_info.device_name);
bufs.insert(RESBUF_ID_FIRMWARE_MAJOR, vec![dev_info.version_major]);
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ pub struct KeyHandle {
pub transports: AuthenticatorTransports,
}

pub type AppId = Vec<u8>;
#[derive(Clone, Debug)]
pub struct AppId(Vec<u8>);
pub type RegisterResult = (Vec<u8>, u2ftypes::U2FDeviceInfo);
pub type SignResult = (AppId, Vec<u8>, Vec<u8>, u2ftypes::U2FDeviceInfo);

Expand Down
8 changes: 1 addition & 7 deletions src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl AuthenticatorTransport for U2FManager {
status: Sender<crate::StatusUpdate>,
callback: StateCallback<crate::Result<crate::RegisterResult>>,
) -> crate::Result<()> {
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
if challenge.len() != PARAMETER_SIZE {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}

Expand Down Expand Up @@ -161,12 +161,6 @@ impl AuthenticatorTransport for U2FManager {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}

for app_id in &app_ids {
if app_id.len() != PARAMETER_SIZE {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
}
}

for key_handle in &key_handles {
if key_handle.credential.len() > 256 {
return Err(AuthenticatorError::InvalidRelyingPartyInput);
Expand Down
15 changes: 11 additions & 4 deletions src/statemachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,14 @@ impl StateMachine {
},
);

let app_id_hash = application.to_u2f();

// Iterate the exclude list and see if there are any matches.
// If so, we'll keep polling the device anyway to test for user
// consent, to be consistent with CTAP2 device behavior.
let excluded = key_handles.iter().any(|key_handle| {
is_valid_transport(key_handle.transports)
&& u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
&& u2f_is_keyhandle_valid(dev, &challenge, &app_id_hash, &key_handle.credential)
.unwrap_or(false) /* no match on failure */
});

Expand All @@ -130,7 +132,7 @@ impl StateMachine {
)));
break;
}
} else if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
} else if let Ok(bytes) = u2f_register(dev, &challenge, &app_id_hash) {
let dev_info = dev.get_device_info();
send_status(
&status_mutex,
Expand Down Expand Up @@ -201,7 +203,9 @@ impl StateMachine {
// valid key handle for an appId, we'll use that appId below.
let (app_id, valid_handles) =
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
u2f_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
let app_id: crate::AppId = app_id.clone().into();
let app_id_hash = app_id.to_u2f();
u2f_is_keyhandle_valid(dev, &challenge, &app_id_hash, &key_handle.credential)
.unwrap_or(false) /* no match on failure */
});

Expand All @@ -225,6 +229,8 @@ impl StateMachine {
},
);

let app_id_hash = app_id.to_u2f();

'outer: while alive() {
// If the device matches none of the given key handles
// then just make it blink with bogus data.
Expand All @@ -239,7 +245,8 @@ impl StateMachine {
} else {
// Otherwise, try to sign.
for key_handle in &valid_handles {
if let Ok(bytes) = u2f_sign(dev, &challenge, app_id, &key_handle.credential)
if let Ok(bytes) =
u2f_sign(dev, &challenge, &app_id_hash, &key_handle.credential)
{
let dev_info = dev.get_device_info();
send_status(
Expand Down
26 changes: 26 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

extern crate libc;

use sha2::Digest;
use std::io;
use std::ops::Deref;

use crate::AppId;

macro_rules! try_or {
($val:expr, $or:expr) => {
Expand Down Expand Up @@ -65,3 +69,25 @@ pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
pub fn io_err(msg: &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, msg)
}

impl AppId {
pub fn to_u2f(&self) -> Vec<u8> {
let mut sha256 = sha2::Sha256::default();
sha256.input(&self.0);
sha256.result().to_vec()
}
}

impl Deref for AppId {
type Target = Vec<u8>;

fn deref(&self) -> &Vec<u8> {
&self.0
}
}

impl From<Vec<u8>> for AppId {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
Loading