Check PIN policy before requesting PIN
Closes str4d/age-plugin-yubikey#34.
This commit is contained in:
+1
-1
@@ -213,7 +213,7 @@ impl IdentityPluginV1 for IdentityPlugin {
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = conn.request_pin(&mut callbacks)? {
|
||||
if let Err(e) = conn.request_pin_if_necessary(&mut callbacks)? {
|
||||
callbacks.error(e)?.unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ pub(crate) struct Metadata {
|
||||
slot: RetiredSlotId,
|
||||
name: String,
|
||||
created: String,
|
||||
pin_policy: Option<PinPolicy>,
|
||||
pub(crate) pin_policy: Option<PinPolicy>,
|
||||
touch_policy: Option<TouchPolicy>,
|
||||
}
|
||||
|
||||
|
||||
+30
-5
@@ -18,6 +18,7 @@ use std::time::{Duration, SystemTime};
|
||||
use yubikey_piv::{
|
||||
certificate::{Certificate, PublicKeyInfo},
|
||||
key::{decrypt_data, AlgorithmId, RetiredSlotId, SlotId},
|
||||
policy::PinPolicy,
|
||||
readers::Reader,
|
||||
yubikey::Serial,
|
||||
MgmKey, Readers, YubiKey,
|
||||
@@ -27,6 +28,7 @@ use crate::{
|
||||
error::Error,
|
||||
format::{RecipientLine, STANZA_KEY_LABEL},
|
||||
p256::{Recipient, TAG_BYTES},
|
||||
util::Metadata,
|
||||
IDENTITY_PREFIX,
|
||||
};
|
||||
|
||||
@@ -299,12 +301,12 @@ impl Stub {
|
||||
};
|
||||
|
||||
// Read the pubkey from the YubiKey slot and check it still matches.
|
||||
let pk = match Certificate::read(&mut yubikey, SlotId::Retired(self.slot))
|
||||
let (cert, pk) = match Certificate::read(&mut yubikey, SlotId::Retired(self.slot))
|
||||
.ok()
|
||||
.and_then(|cert| match cert.subject_pki() {
|
||||
PublicKeyInfo::EcP256(pubkey) => {
|
||||
Recipient::from_encoded(pubkey).filter(|pk| pk.tag() == self.tag)
|
||||
}
|
||||
PublicKeyInfo::EcP256(pubkey) => Recipient::from_encoded(pubkey)
|
||||
.filter(|pk| pk.tag() == self.tag)
|
||||
.map(|pk| (cert, pk)),
|
||||
_ => None,
|
||||
}) {
|
||||
Some(pk) => pk,
|
||||
@@ -318,6 +320,7 @@ impl Stub {
|
||||
|
||||
Ok(Ok(Connection {
|
||||
yubikey,
|
||||
cert,
|
||||
pk,
|
||||
slot: self.slot,
|
||||
tag: self.tag,
|
||||
@@ -328,6 +331,7 @@ impl Stub {
|
||||
|
||||
pub(crate) struct Connection {
|
||||
yubikey: YubiKey,
|
||||
cert: Certificate,
|
||||
pk: Recipient,
|
||||
slot: RetiredSlotId,
|
||||
tag: [u8; 4],
|
||||
@@ -339,10 +343,31 @@ impl Connection {
|
||||
&self.pk
|
||||
}
|
||||
|
||||
pub(crate) fn request_pin<E>(
|
||||
pub(crate) fn request_pin_if_necessary<E>(
|
||||
&mut self,
|
||||
callbacks: &mut dyn Callbacks<E>,
|
||||
) -> io::Result<Result<(), identity::Error>> {
|
||||
// Check if we can skip requesting a PIN.
|
||||
let (_, cert) = x509_parser::parse_x509_certificate(self.cert.as_ref()).unwrap();
|
||||
match Metadata::extract(&mut self.yubikey, self.slot, &cert, true) {
|
||||
Some(metadata) => {
|
||||
if let Some(PinPolicy::Never) = metadata.pin_policy {
|
||||
return Ok(Ok(()));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: format!(
|
||||
"Certificate for YubiKey identity contains an invalid PIN policy"
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// The policy requires a PIN, so request it.
|
||||
// Note that we can't distinguish between PinPolicy::Once and PinPolicy::Always
|
||||
// because this plugin is ephemeral, so we always request the PIN.
|
||||
let pin = match callbacks.request_secret(&format!(
|
||||
"Enter PIN for YubiKey with serial {}",
|
||||
self.yubikey.serial()
|
||||
|
||||
Reference in New Issue
Block a user