extern crate ctap_hmac as ctap; extern crate pamsm; mod pamfido2; use ctap::{make_credential_devices, FidoCredentialRequestBuilder, FidoDevice}; use hex; use pamfido2::PamFido2; use failure::ResultExt; use std::error::Error; use std::io::stdout; use std::io::Write; use std::path::PathBuf; use structopt; use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(name = "fido2pam")] struct CliOpt { #[structopt(subcommand)] action: Action, } #[derive(Debug, StructOpt)] enum Action { /// Generate a new credential Credential { /// Whether the authenticator should promt the user for verification #[structopt(short, long = "user-verification")] uv: bool, /// Username for which the credential will be requested username: String, }, /// Test your config Test { /// Path to the file containing the credentials generated by `credential` auth_file: PathBuf, /// The username to test for username: String, }, } fn main() -> Result<(), Box> { match CliOpt::from_args().action { Action::Credential { uv, username } => { let mut devices = ctap::get_devices() .compat()? .filter_map(|handle| FidoDevice::new(&handle).ok()) .collect::>(); let req = FidoCredentialRequestBuilder::default() .rp_id("fido2pam") .user_name(username.as_ref()) .uv(uv) .build() .unwrap(); let cred = make_credential_devices(&req, devices.iter_mut()).compat()?; stdout() .write_all( &[ &username, &hex::encode(&cred.id)[..], &hex::encode(&cred.public_key.unwrap())[..], ] .join(":") .as_bytes(), ) .unwrap(); } Action::Test { auth_file: authfile, username, } => { let res = PamFido2.fido_authenticate( &username, vec![["authfile=", authfile.to_str().unwrap()].join("")], )?; println!("{}", res) } }; Ok(()) }