Implement User Interaction Flag (UIF)

This commit is contained in:
Heiko Schaefer 2022-04-22 15:21:22 +02:00
parent c4572de046
commit 20ebac295d
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
2 changed files with 200 additions and 21 deletions

View file

@ -177,26 +177,42 @@ impl ApplicationRelatedData {
// fn key_information() { // fn key_information() {
// unimplemented!() // unimplemented!()
// } // }
//
// #[allow(dead_code)] pub fn uif_pso_cds(&self) -> Result<Option<UIF>, Error> {
// fn uif_pso_cds() { let uif = self.0.find(&[0xd6].into());
// unimplemented!()
// } match uif {
// None => Ok(None),
// #[allow(dead_code)] Some(v) => Ok(Some(v.serialize().try_into()?)),
// fn uif_pso_dec() { }
// unimplemented!() }
// }
// pub fn uif_pso_dec(&self) -> Result<Option<UIF>, Error> {
// #[allow(dead_code)] let uif = self.0.find(&[0xd7].into());
// fn uif_pso_aut() {
// unimplemented!() match uif {
// } None => Ok(None),
// Some(v) => Ok(Some(v.serialize().try_into()?)),
// #[allow(dead_code)] }
// fn uif_attestation() { }
// unimplemented!()
// } pub fn uif_pso_aut(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd8].into());
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd9].into());
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
} }
/// Security support template (see spec pg. 24) /// Security support template (see spec pg. 24)
@ -230,6 +246,145 @@ impl KeyGenerationTime {
} }
} }
/// User Interaction Flag (UIF) (see spec pg. 24)
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct UIF([u8; 2]);
impl TryFrom<Vec<u8>> for UIF {
type Error = Error;
fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
if v.len() == 2 {
Ok(UIF(v.try_into().unwrap()))
} else {
Err(Error::ParseError(format!("Can't get UID from {:x?}", v)))
}
}
}
impl UIF {
pub fn touch_policy(&self) -> TouchPolicy {
self.0[0].into()
}
pub fn set_touch_policy(&mut self, tm: TouchPolicy) {
self.0[0] = tm.into();
}
pub fn features(&self) -> Features {
self.0[1].into()
}
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.0[..]
}
}
impl Display for UIF {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Touch policy: {} [Features: {}]",
self.touch_policy(),
self.features()
)
}
}
/// User interaction setting.
///
/// See spec pg 24 and https://github.com/Yubico/yubikey-manager/blob/main/ykman/openpgp.py
#[non_exhaustive]
pub enum TouchPolicy {
Off,
On,
Fixed,
Cached,
CachedFixed,
Unknown(u8),
}
impl Display for TouchPolicy {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TouchPolicy::Off => write!(f, "Off"),
TouchPolicy::On => write!(f, "On"),
TouchPolicy::Fixed => write!(f, "Fixed"),
TouchPolicy::Cached => write!(f, "Cached"),
TouchPolicy::CachedFixed => write!(f, "CachedFixed"),
TouchPolicy::Unknown(i) => write!(f, "Unknown({})", i),
}
}
}
impl From<TouchPolicy> for u8 {
fn from(tm: TouchPolicy) -> Self {
match tm {
TouchPolicy::Off => 0,
TouchPolicy::On => 1,
TouchPolicy::Fixed => 2,
TouchPolicy::Cached => 3,
TouchPolicy::CachedFixed => 4,
TouchPolicy::Unknown(i) => i,
}
}
}
impl From<u8> for TouchPolicy {
fn from(i: u8) -> Self {
match i {
0 => TouchPolicy::Off,
1 => TouchPolicy::On,
2 => TouchPolicy::Fixed,
3 => TouchPolicy::Cached,
4 => TouchPolicy::CachedFixed,
_ => TouchPolicy::Unknown(i),
}
}
}
/// "additional hardware for user interaction" (see spec 4.1.3.2)
pub struct Features(u8);
impl From<u8> for Features {
fn from(i: u8) -> Self {
Features(i)
}
}
impl Display for Features {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut ft = vec![];
if self.0 & 0x80 != 0 {
ft.push("Display")
}
if self.0 & 0x40 != 0 {
ft.push("Biometric input sensor")
}
if self.0 & 0x20 != 0 {
ft.push("Button")
}
if self.0 & 0x10 != 0 {
ft.push("Keypad")
}
if self.0 & 0x8 != 0 {
ft.push("LED")
}
if self.0 & 0x4 != 0 {
ft.push("Loudspeaker")
}
if self.0 & 0x2 != 0 {
ft.push("Microphone")
}
if self.0 & 0x1 != 0 {
ft.push("Touchscreen")
}
write!(f, "{}", ft.join(", "))
}
}
/// 4.2.1 Application Identifier (AID) /// 4.2.1 Application Identifier (AID)
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct ApplicationIdentifier { pub struct ApplicationIdentifier {

View file

@ -8,7 +8,7 @@ use crate::apdu::commands;
use crate::apdu::response::RawResponse; use crate::apdu::response::RawResponse;
use crate::card_do::{ use crate::card_do::{
ApplicationRelatedData, CardholderRelatedData, Fingerprint, KeyGenerationTime, Lang, ApplicationRelatedData, CardholderRelatedData, Fingerprint, KeyGenerationTime, Lang,
PWStatusBytes, SecuritySupportTemplate, Sex, PWStatusBytes, SecuritySupportTemplate, Sex, UIF,
}; };
use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial}; use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial};
use crate::tlv::{value::Value, Tlv}; use crate::tlv::{value::Value, Tlv};
@ -719,6 +719,30 @@ impl<'a> OpenPgpTransaction<'a> {
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
/// Set UIF for PSO:CDS
pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_cds");
let cmd = commands::put_data(&[0xd6], uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into()
}
/// Set UIF for PSO:DEC
pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_dec");
let cmd = commands::put_data(&[0xd7], uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into()
}
/// Set UIF for PSO:AUT
pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_aut");
let cmd = commands::put_data(&[0xd8], uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into()
}
/// Import an existing private key to the card. /// Import an existing private key to the card.
/// (This implicitly sets the algorithm info, fingerprint and timestamp) /// (This implicitly sets the algorithm info, fingerprint and timestamp)
pub fn key_import( pub fn key_import(