diff --git a/Cargo.lock b/Cargo.lock index 4c54bf0..60efea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -351,7 +351,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -409,7 +409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.5", ] [[package]] @@ -502,8 +502,11 @@ version = "0.2.18" dependencies = [ "native-pkcs11-traits", "p256", + "pinentry", "rand", + "rpassword", "rsa", + "secrecy", "serial_test", "spki", "thiserror", @@ -646,7 +649,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.5", ] [[package]] @@ -687,12 +690,32 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pinentry" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5b8bc68be6a5e2ba84ee86db53f816cba1905b94fcb7c236e606221cc8fc8" +dependencies = [ + "log", + "nom", + "percent-encoding", + "secrecy", + "which", + "zeroize", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -863,6 +886,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + [[package]] name = "rsa" version = "0.9.6" @@ -884,6 +918,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -900,7 +944,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1307,13 +1351,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1322,28 +1390,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -1356,24 +1442,48 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/native-pkcs11-piv/Cargo.toml b/native-pkcs11-piv/Cargo.toml index 2834513..fcb9bbf 100644 --- a/native-pkcs11-piv/Cargo.toml +++ b/native-pkcs11-piv/Cargo.toml @@ -15,8 +15,11 @@ p256 = { version = "0.13.2", default-features = false, features = [ "pkcs8", "std", ] } +pinentry = "0.5.0" rand = "0.8.5" +rpassword = "7.3.1" rsa = { version = "0.9.6", default-features = false, features = ["std"] } +secrecy = "0.8.0" # TODO: temporary workaround for RustCrypto/traits#1262, remove after upgrading # the p256 package past 0.13.0. spki = { version = "0.7.3", features = ["std"] } diff --git a/native-pkcs11-piv/src/piv/backend.rs b/native-pkcs11-piv/src/piv/backend.rs index 835428d..c07b7af 100644 --- a/native-pkcs11-piv/src/piv/backend.rs +++ b/native-pkcs11-piv/src/piv/backend.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use tracing::instrument; use yubikey::YubiKey; @@ -27,14 +27,49 @@ use native_pkcs11_traits::Result as P11Result; #[derive(Debug, Default)] pub struct YubikeyPivBackend { - yubikey: Option, + cached_pin: Mutex>, + yubikey: Mutex>, } impl YubikeyPivBackend { pub fn new() -> Self { - YubikeyPivBackend { - yubikey: None + YubikeyPivBackend::default() + } + + fn run_with_yubikey(&self, verify: bool, mut callback: F) -> P11Result<()> + where + F: FnMut(&mut YubiKey) -> P11Result<()>, + { + let mut yubikey = self.yubikey.lock().unwrap(); + if yubikey.is_none() { + *yubikey = Some(YubiKey::open()?); } + let mut yk = yubikey.as_mut().unwrap(); + if verify { + let pin = self.prepare_pin()?; + let verify_result = yk.verify_pin(pin.as_bytes()); + if verify_result.is_err() { + self.clear_pin(); + } + verify_result?; + } + callback(&mut yk) + } + + fn clear_pin(&self) -> () { + let mut cached_pin = self.cached_pin.lock().unwrap(); + if cached_pin.is_some() { + *cached_pin = None; + } + } + + fn prepare_pin(&self) -> P11Result { + let mut cached_pin = self.cached_pin.lock().unwrap(); + if cached_pin.is_none() { + let pin = crate::piv::pinentry::get_pin()?; + *cached_pin = Some(pin); + } + Ok(cached_pin.as_deref().unwrap().to_string()) } } diff --git a/native-pkcs11-piv/src/piv/mod.rs b/native-pkcs11-piv/src/piv/mod.rs index 0e9687b..3dca96d 100644 --- a/native-pkcs11-piv/src/piv/mod.rs +++ b/native-pkcs11-piv/src/piv/mod.rs @@ -23,6 +23,7 @@ use native_pkcs11_traits::SignatureAlgorithm; mod backend; pub mod certificate; pub mod key; +mod pinentry; pub type Result = std::result::Result; diff --git a/native-pkcs11-piv/src/piv/pinentry.rs b/native-pkcs11-piv/src/piv/pinentry.rs new file mode 100644 index 0000000..253e8fb --- /dev/null +++ b/native-pkcs11-piv/src/piv/pinentry.rs @@ -0,0 +1,51 @@ +use std::{env, fs}; + +use pinentry::PassphraseInput; +use secrecy::ExposeSecret; + +const PIN_ENTRY_ENV: &str = "PIN_ENTRY_CMD"; +const PIN_ENTRY_1: &str = "/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac"; +const PIN_ENTRY_DEFAULT: &str = "pinentry"; + +pub fn get_pin() -> Result { + let pin_entry = get_pin_entry(); + let description = "Please input PIN"; + let prompt = "PIN: "; + + if let Some(mut input) = PassphraseInput::with_binary(pin_entry) { + let secret = input + .with_description(&format!("{}.", description)) + .with_prompt(prompt) + .interact(); + + match secret { + Ok(secret_string) => { + Ok(secret_string.expose_secret().to_string()) + } + Err(e) => { + Err(format!("Read PIN failed: {}", e)) + } + } + } else { + match rpassword::prompt_password(format!("{}: ", description)) { + Ok(pin) => { + Ok(pin) + } + Err(e) => { + Err(format!("Read PIN2 failed: {}", e)) + } + } + } +} + +fn get_pin_entry() -> String { + if let Ok(pin_entry) = env::var(PIN_ENTRY_ENV) { + return pin_entry; + } + if let Ok(m) = fs::metadata(PIN_ENTRY_1) { + if m.is_file() { + return PIN_ENTRY_1.to_string(); + } + } + PIN_ENTRY_DEFAULT.to_string() +} \ No newline at end of file