Check PIN policy before requesting PIN

Closes str4d/age-plugin-yubikey#34.
This commit is contained in:
Jack Grigg
2021-08-20 15:11:39 +01:00
parent 30f4d00902
commit 2c90195f99
3 changed files with 32 additions and 7 deletions
+1 -1
View File
@@ -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(); callbacks.error(e)?.unwrap();
continue; continue;
} }
+1 -1
View File
@@ -96,7 +96,7 @@ pub(crate) struct Metadata {
slot: RetiredSlotId, slot: RetiredSlotId,
name: String, name: String,
created: String, created: String,
pin_policy: Option<PinPolicy>, pub(crate) pin_policy: Option<PinPolicy>,
touch_policy: Option<TouchPolicy>, touch_policy: Option<TouchPolicy>,
} }
+30 -5
View File
@@ -18,6 +18,7 @@ use std::time::{Duration, SystemTime};
use yubikey_piv::{ use yubikey_piv::{
certificate::{Certificate, PublicKeyInfo}, certificate::{Certificate, PublicKeyInfo},
key::{decrypt_data, AlgorithmId, RetiredSlotId, SlotId}, key::{decrypt_data, AlgorithmId, RetiredSlotId, SlotId},
policy::PinPolicy,
readers::Reader, readers::Reader,
yubikey::Serial, yubikey::Serial,
MgmKey, Readers, YubiKey, MgmKey, Readers, YubiKey,
@@ -27,6 +28,7 @@ use crate::{
error::Error, error::Error,
format::{RecipientLine, STANZA_KEY_LABEL}, format::{RecipientLine, STANZA_KEY_LABEL},
p256::{Recipient, TAG_BYTES}, p256::{Recipient, TAG_BYTES},
util::Metadata,
IDENTITY_PREFIX, IDENTITY_PREFIX,
}; };
@@ -299,12 +301,12 @@ impl Stub {
}; };
// Read the pubkey from the YubiKey slot and check it still matches. // 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() .ok()
.and_then(|cert| match cert.subject_pki() { .and_then(|cert| match cert.subject_pki() {
PublicKeyInfo::EcP256(pubkey) => { PublicKeyInfo::EcP256(pubkey) => Recipient::from_encoded(pubkey)
Recipient::from_encoded(pubkey).filter(|pk| pk.tag() == self.tag) .filter(|pk| pk.tag() == self.tag)
} .map(|pk| (cert, pk)),
_ => None, _ => None,
}) { }) {
Some(pk) => pk, Some(pk) => pk,
@@ -318,6 +320,7 @@ impl Stub {
Ok(Ok(Connection { Ok(Ok(Connection {
yubikey, yubikey,
cert,
pk, pk,
slot: self.slot, slot: self.slot,
tag: self.tag, tag: self.tag,
@@ -328,6 +331,7 @@ impl Stub {
pub(crate) struct Connection { pub(crate) struct Connection {
yubikey: YubiKey, yubikey: YubiKey,
cert: Certificate,
pk: Recipient, pk: Recipient,
slot: RetiredSlotId, slot: RetiredSlotId,
tag: [u8; 4], tag: [u8; 4],
@@ -339,10 +343,31 @@ impl Connection {
&self.pk &self.pk
} }
pub(crate) fn request_pin<E>( pub(crate) fn request_pin_if_necessary<E>(
&mut self, &mut self,
callbacks: &mut dyn Callbacks<E>, callbacks: &mut dyn Callbacks<E>,
) -> io::Result<Result<(), identity::Error>> { ) -> 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!( let pin = match callbacks.request_secret(&format!(
"Enter PIN for YubiKey with serial {}", "Enter PIN for YubiKey with serial {}",
self.yubikey.serial() self.yubikey.serial()