feat: update pkcs11 piv

This commit is contained in:
2024-07-07 10:15:23 +08:00
parent 781d173b86
commit e1e72ed097
5 changed files with 217 additions and 17 deletions

View File

@@ -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"] }

View File

@@ -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<YubiKey>,
cached_pin: Mutex<Option<String>>,
yubikey: Mutex<Option<YubiKey>>,
}
impl YubikeyPivBackend {
pub fn new() -> Self {
YubikeyPivBackend {
yubikey: None
YubikeyPivBackend::default()
}
fn run_with_yubikey<F>(&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<String> {
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())
}
}

View File

@@ -23,6 +23,7 @@ use native_pkcs11_traits::SignatureAlgorithm;
mod backend;
pub mod certificate;
pub mod key;
mod pinentry;
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -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<String, String> {
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()
}