Disconnect without resetting YubiKeys if it is safe to do so
This enables the PIN caches to be preserved across age-plugin-yubikey processes, allowing PIN policies of "once" to become meaningful.
This commit is contained in:
Generated
+1
-2
@@ -2122,8 +2122,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "yubikey"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10e6fa9476951a9b93d9a31aa5554b5bbac7aafdc5b23e663eb3f9b635c86053"
|
||||
source = "git+https://github.com/iqlusioninc/yubikey.rs.git?rev=1d33ea174791f699dfc0e6a06648aa3d9e066144#1d33ea174791f699dfc0e6a06648aa3d9e066144"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"chrono",
|
||||
|
||||
@@ -53,3 +53,6 @@ sysinfo = "0.27"
|
||||
[dev-dependencies]
|
||||
flate2 = "1"
|
||||
man = "0.3"
|
||||
|
||||
[patch.crates-io]
|
||||
yubikey = { git = "https://github.com/iqlusioninc/yubikey.rs.git", rev = "1d33ea174791f699dfc0e6a06648aa3d9e066144" }
|
||||
|
||||
+24
@@ -236,6 +236,23 @@ pub(crate) fn open(serial: Option<Serial>) -> Result<YubiKey, Error> {
|
||||
Ok(yubikey)
|
||||
}
|
||||
|
||||
/// Disconnect from the YubiKey without resetting it.
|
||||
///
|
||||
/// This can be used to preserve the YubiKey's PIN and touch caches. There are two cases
|
||||
/// where we want to do this:
|
||||
///
|
||||
/// - We connected to this YubiKey in a read-only context, so we have not made any changes
|
||||
/// to the YubiKey's state. However, we might have asked an agent to release the YubiKey
|
||||
/// in `key::open_connection`, and we want to allow any state it may have left behind
|
||||
/// (such as cached PINs or touches) to persist beyond our execution, for usability.
|
||||
/// - We opened this connection in a decryption context, so the only changes to the
|
||||
/// YubiKey's state were to potentially cache the PIN and/or touch (depending on the
|
||||
/// policies of the slot). We want to allow these to persist beyond our execution, for
|
||||
/// usability.
|
||||
pub(crate) fn disconnect_without_reset(yubikey: YubiKey) {
|
||||
let _ = yubikey.disconnect(pcsc::Disposition::LeaveCard);
|
||||
}
|
||||
|
||||
pub(crate) fn manage(yubikey: &mut YubiKey) -> Result<(), Error> {
|
||||
const DEFAULT_PIN: &str = "123456";
|
||||
const DEFAULT_PUK: &str = "12345678";
|
||||
@@ -674,6 +691,13 @@ impl Connection {
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Close this connection without resetting the YubiKey.
|
||||
///
|
||||
/// This can be used to preserve the YubiKey's PIN and touch caches.
|
||||
pub(crate) fn disconnect_without_reset(self) {
|
||||
disconnect_without_reset(self.yubikey);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+18
-2
@@ -181,6 +181,13 @@ fn generate(flags: PluginFlags) -> Result<(), Error> {
|
||||
|
||||
util::print_identity(stub, recipient, metadata);
|
||||
|
||||
// We have written to the YubiKey, which means we've authenticated with the management
|
||||
// key. Out of an abundance of caution, we let the YubiKey be reset on disconnect,
|
||||
// which will clear its PIN and touch caches. This has as small negative UX effect,
|
||||
// but identity generation is a relatively infrequent occurrence, and users are more
|
||||
// likely to see their cached PINs reset due to switching applets (e.g. from PIV to
|
||||
// FIDO2).
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -200,6 +207,8 @@ fn print_single(
|
||||
|
||||
printer(stub, recipient, metadata);
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -233,6 +242,8 @@ fn print_multiple(
|
||||
println!();
|
||||
}
|
||||
println!();
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
}
|
||||
if printed > 1 {
|
||||
eprintln!("{}", fl!("printed-multiple", kind = kind, count = printed));
|
||||
@@ -360,11 +371,13 @@ fn main() -> Result<(), Error> {
|
||||
.iter()
|
||||
.map(|reader| {
|
||||
key::open_connection(reader).map(|yk| {
|
||||
fl!(
|
||||
let name = fl!(
|
||||
"cli-setup-yk-name",
|
||||
yubikey_name = reader.name(),
|
||||
yubikey_serial = yk.serial().to_string(),
|
||||
)
|
||||
);
|
||||
key::disconnect_without_reset(yk);
|
||||
name
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
@@ -457,8 +470,10 @@ fn main() -> Result<(), Error> {
|
||||
util::Metadata::extract(&mut yubikey, slot, key.certificate(), true)
|
||||
.unwrap();
|
||||
|
||||
key::disconnect_without_reset(yubikey);
|
||||
((stub, recipient, metadata), false)
|
||||
} else {
|
||||
key::disconnect_without_reset(yubikey);
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
@@ -540,6 +555,7 @@ fn main() -> Result<(), Error> {
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
key::disconnect_without_reset(yubikey);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +249,8 @@ impl IdentityPluginV1 for IdentityPlugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.disconnect_without_reset();
|
||||
}
|
||||
Ok(file_keys)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user