diff --git a/Cargo.lock b/Cargo.lock index 975a2b5..f3bf5ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -489,6 +489,7 @@ name = "fido_ssh_maker" version = "0.1.0" dependencies = [ "anyhow", + "bitflags", "clap", "ctap-hid-fido2", "rpassword", diff --git a/Cargo.toml b/Cargo.toml index cfb2c1c..a28f57d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.72" +bitflags = "2.4.0" clap = { version = "4.3.21", features = ["derive"] } ctap-hid-fido2 = "3.5.0" rpassword = "7.2.0" diff --git a/src/main.rs b/src/main.rs index a21da45..628e8e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,29 @@ use std::{ fs::Permissions, os::unix::prelude::PermissionsExt, - path::{Path, PathBuf}, fmt::Display, io::Write, + path::{Path, PathBuf}, fmt::Display, }; use anyhow::anyhow; +use bitflags::bitflags; use clap::{Parser, ValueEnum}; use ctap_hid_fido2::{ - fidokey::{CredentialSupportedKeyType, MakeCredentialArgsBuilder, CredentialExtension, credential_management::credential_management_params::CredentialProtectionPolicy}, + fidokey::{CredentialSupportedKeyType, MakeCredentialArgsBuilder}, verifier, FidoKeyHidFactory, LibCfg, }; use ssh_encoding::{Decode, Encode, LineEnding}; use ssh_key::{private, PrivateKey}; +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct SshSkFlags: u8 { + const UserPresenceRequired = 0x01; + const UserVerificationRequired = 0x4; + const ForceOperation = 0x10; + const ResidentKey = 0x20; + } +} + /// Generate FIDO-backed SSH keys #[derive(Parser, Debug)] #[command(author, version, about, long_about = None, arg_required_else_help = true)] @@ -84,20 +95,18 @@ fn main() -> anyhow::Result<()> { let device_has_pin = device.get_info()?.options.contains(&("clientPin".into(), true)); let pin = if device_has_pin { - rpassword::prompt_password("Enter FIDO2 PIN:")? + rpassword::prompt_password("Enter FIDO2 PIN: ")? } else { String::new() }; let make_credential_args = if device_has_pin { MakeCredentialArgsBuilder::new("ssh:", &challenge) .pin(&pin) - .extensions(&[CredentialExtension::CredProtect(Some(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList))]) .key_type(args.key_type.into()) .build() } else { MakeCredentialArgsBuilder::new("ssh:", &challenge) .without_pin_and_uv() - .extensions(&[CredentialExtension::CredProtect(Some(CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList))]) .key_type(args.key_type.into()) .build() }; @@ -117,11 +126,9 @@ fn main() -> anyhow::Result<()> { .der .encode(&mut privkey_bytes)?; "ssh:".encode(&mut privkey_bytes)?; - let flags = (attestation.flags_user_present_result as u8) - | (attestation.flags_user_verified_result as u8) << 2 - | (attestation.flags_attested_credential_data_included as u8) << 6 - | (attestation.flags_extension_data_included as u8) << 7; - privkey_bytes.push(flags); + let mut flags = SshSkFlags::UserPresenceRequired; + flags.set(SshSkFlags::UserVerificationRequired, args.user_verify); + privkey_bytes.push(flags.bits()); verify_result.credential_id.encode(&mut privkey_bytes)?; "".encode(&mut privkey_bytes)?; let privkey = match args.key_type {