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
+30 -5
View File
@@ -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()