From 8c58b447bdf316e18e7d8fa9662860f7df56a185 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Mon, 17 Aug 2020 16:03:55 -0700 Subject: [PATCH] MacOS TouchID token WIP --- Cargo.toml | 6 +- examples/macos_touchid.entitlements.plist | 12 ++ examples/main.rs | 9 ++ src/authenticatorservice.rs | 11 ++ src/virtualdevices/macos_touchid/mod.rs | 6 + .../macos_touchid/touchid_token.rs | 127 ++++++++++++++++++ src/virtualdevices/mod.rs | 3 + 7 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 examples/macos_touchid.entitlements.plist create mode 100644 src/virtualdevices/macos_touchid/mod.rs create mode 100644 src/virtualdevices/macos_touchid/touchid_token.rs diff --git a/Cargo.toml b/Cargo.toml index 3ca8195..759f216 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ maintenance = { status = "actively-developed" } [features] binding-recompile = ["bindgen"] -webdriver = ["base64", "warp", "tokio", "serde"] +webdriver = ["warp", "tokio", "serde"] [target.'cfg(target_os = "linux")'.dependencies] libudev = "^0.2" @@ -25,6 +25,7 @@ devd-rs = "0.3" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9" +keychain-services = "0.1.1" [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "^0.3" @@ -45,13 +46,12 @@ log = "0.4" libc = "0.2" runloop = "0.1.0" bitflags = "1.0" +base64 = "^0.10" tokio = { version = "0.2", optional = true, features = ["macros"] } warp = { version = "0.2.2", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -base64 = { version = "^0.10", optional = true } [dev-dependencies] sha2 = "^0.8.2" -base64 = "^0.10" env_logger = "^0.6" getopts = "^0.2" diff --git a/examples/macos_touchid.entitlements.plist b/examples/macos_touchid.entitlements.plist new file mode 100644 index 0000000..4c4ed69 --- /dev/null +++ b/examples/macos_touchid.entitlements.plist @@ -0,0 +1,12 @@ + + + + + com.apple.application-identifier + com.mozilla.authenticator-rs + keychain-access-groups + + com.mozilla.authenticator-rs + + + \ No newline at end of file diff --git a/examples/main.rs b/examples/main.rs index 33abf2f..611571b 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -40,6 +40,8 @@ fn main() { let mut opts = Options::new(); opts.optflag("x", "no-u2f-usb-hid", "do not enable u2f-usb-hid platforms"); + #[cfg(target_os = "macos")] + opts.optflag("t", "touchid", "enable MacOS touchID device"); #[cfg(feature = "webdriver")] opts.optflag("w", "webdriver", "enable WebDriver virtual bus"); @@ -60,6 +62,13 @@ fn main() { manager.add_u2f_usb_hid_platform_transports(); } + #[cfg(target_os = "macos")] + { + if matches.opt_present("touchid") { + manager.add_macos_touchid_virtual_device(); + } + } + #[cfg(feature = "webdriver")] { if matches.opt_present("webdriver") { diff --git a/src/authenticatorservice.rs b/src/authenticatorservice.rs index d414b74..d57d0be 100644 --- a/src/authenticatorservice.rs +++ b/src/authenticatorservice.rs @@ -70,6 +70,9 @@ impl AuthenticatorService { /// Add any detected platform transports pub fn add_detected_transports(&mut self) { self.add_u2f_usb_hid_platform_transports(); + + #[cfg(target_os = "macos")] + self.add_macos_touchid_virtual_device(); } fn add_transport(&mut self, boxed_token: Box) { @@ -83,6 +86,14 @@ impl AuthenticatorService { } } + #[cfg(target_os = "macos")] + pub fn add_macos_touchid_virtual_device(&mut self) { + match crate::virtualdevices::macos_touchid::TouchIDToken::new() { + Ok(token) => self.add_transport(Box::new(token)), + Err(e) => error!("Could not add MacOS TouchID virtual device: {}", e), + } + } + #[cfg(feature = "webdriver")] pub fn add_webdriver_virtual_bus(&mut self) { match crate::virtualdevices::webdriver::VirtualManager::new() { diff --git a/src/virtualdevices/macos_touchid/mod.rs b/src/virtualdevices/macos_touchid/mod.rs new file mode 100644 index 0000000..b12d662 --- /dev/null +++ b/src/virtualdevices/macos_touchid/mod.rs @@ -0,0 +1,6 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +mod touchid_token; +pub use touchid_token::TouchIDToken; diff --git a/src/virtualdevices/macos_touchid/touchid_token.rs b/src/virtualdevices/macos_touchid/touchid_token.rs new file mode 100644 index 0000000..53cc8c4 --- /dev/null +++ b/src/virtualdevices/macos_touchid/touchid_token.rs @@ -0,0 +1,127 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::authenticatorservice::AuthenticatorTransport; +use crate::statecallback::StateCallback; +use crate::virtualdevices::software_u2f::SoftwareU2FToken; +use crate::{AppId, KeyHandle, RegisterFlags, RegisterResult, SignFlags, SignResult, StatusUpdate}; +use base64; +use keychain_services::*; +use rand::{thread_rng, RngCore}; +use std::sync::mpsc::Sender; +use std::{io, thread}; + +const TOUCHID_APP_TAG: [u8; 4] = [0xff, 0xff, 0xff, 0xff]; + +pub struct TouchIDToken { + pub u2f_impl: SoftwareU2FToken, + // pub secret: PasswordData, +} + +fn map_to_ioerr(x: Result) -> Result +where + U: std::fmt::Display, +{ + x.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e))) +} + +impl TouchIDToken { + pub fn new() -> io::Result { + let service = "example.com"; + let account = "example"; + + let keychain = map_to_ioerr(Keychain::find_default())?; + + let mut flags = AccessControlFlags::new(); + flags.add(AccessConstraint::BiometryCurrentSet); + flags.add(AccessOption::PrivateKeyUsage); + + let ac = map_to_ioerr(AccessControl::create_with_flags( + AttrAccessible::WhenPasscodeSetThisDeviceOnly, + flags, + ))?; + + let params = KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256) + .application_tag(AttrApplicationTag::new(&TOUCHID_APP_TAG)) + .access_control(&ac) + .token_id(AttrTokenId::SecureEnclave) + // .permanent(true) + ; + + let keypair = KeyPair::generate(params).unwrap(); + + let public_key_bytes = keypair.public_key.to_external_representation().unwrap(); + println!("pubkey: {}", &base64::encode(&public_key_bytes)); + + // let secret = match GenericPassword::find(&keychain, &service, &account) { + // Ok(recovered_secret) => recovered_secret, + // Err(_) => { + // let mut keymat = [0u8; 32]; + // thread_rng().fill_bytes(&mut keymat); + // map_to_ioerr(GenericPassword::create(&keychain, &service, &account, &base64::encode(&keymat)))? + // } + // }; + + // println!("password: {}", &base64::encode(&map_to_ioerr(secret.password())?)); + + Ok(Self { + u2f_impl: SoftwareU2FToken::new(), + // secret: map_to_ioerr(secret.password())?, + }) + } +} + +impl AuthenticatorTransport for TouchIDToken { + fn register( + &mut self, + flags: RegisterFlags, + timeout: u64, + challenge: Vec, + application: AppId, + key_handles: Vec, + status: Sender, + callback: StateCallback>, + ) -> Result<(), crate::Error> { + let result = self + .u2f_impl + .register(flags, timeout, challenge, application, key_handles); + status + .send(StatusUpdate::Success { + dev_info: self.u2f_impl.dev_info(), + }) + .map_err(|_| crate::Error::Unknown)?; + thread::spawn(move || { + callback.call(result); + }); + Ok(()) + } + + fn sign( + &mut self, + flags: SignFlags, + timeout: u64, + challenge: Vec, + app_ids: Vec, + key_handles: Vec, + status: Sender, + callback: StateCallback>, + ) -> Result<(), crate::Error> { + let result = self + .u2f_impl + .sign(flags, timeout, challenge, app_ids, key_handles); + status + .send(StatusUpdate::Success { + dev_info: self.u2f_impl.dev_info(), + }) + .map_err(|_| crate::Error::Unknown)?; + thread::spawn(move || { + callback.call(result); + }); + Ok(()) + } + + fn cancel(&mut self) -> Result<(), crate::Error> { + Ok(()) + } +} diff --git a/src/virtualdevices/mod.rs b/src/virtualdevices/mod.rs index 5c0a9d3..259db9d 100644 --- a/src/virtualdevices/mod.rs +++ b/src/virtualdevices/mod.rs @@ -2,6 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#[cfg(target_os = "macos")] +pub mod macos_touchid; + #[cfg(feature = "webdriver")] pub mod webdriver;