extended capabilities: restructure, to prepare for different versions of this DO

This commit is contained in:
Heiko Schaefer 2021-09-14 01:50:56 +02:00
parent 245740febc
commit f2e5fea0fc
6 changed files with 86 additions and 116 deletions

View file

@ -14,11 +14,9 @@ use sequoia_openpgp::Cert;
use openpgp_card::algorithm::{Algo, AlgoInfo}; use openpgp_card::algorithm::{Algo, AlgoInfo};
use openpgp_card::card_do::{ use openpgp_card::card_do::{
ApplicationIdentifier, ApplicationRelatedData, CardholderRelatedData, ApplicationIdentifier, ApplicationRelatedData, CardholderRelatedData,
ExCapFeatures, ExtendedCapabilities, ExtendedLengthInfo, Fingerprint, ExtendedCapabilities, ExtendedLengthInfo, Fingerprint, HistoricalBytes,
HistoricalBytes, KeyGenerationTime, PWStatusBytes, KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex,
SecuritySupportTemplate, Sex,
}; };
use openpgp_card::crypto_data::CardUploadableKey;
use openpgp_card::{CardApp, CardClientBox, Error, KeySet, KeyType, Response}; use openpgp_card::{CardApp, CardClientBox, Error, KeySet, KeyType, Response};
use crate::decryptor::CardDecryptor; use crate::decryptor::CardDecryptor;
@ -245,7 +243,7 @@ impl Open {
// The DO "Algorithm Information" (Tag FA) shall be present if // The DO "Algorithm Information" (Tag FA) shall be present if
// Algorithm attributes can be changed // Algorithm attributes can be changed
let ec = self.extended_capabilities()?; let ec = self.extended_capabilities()?;
if !ec.features().contains(&ExCapFeatures::AlgoAttrsChangeable) { if !ec.algo_attrs_changeable() {
// Algorithm attributes can not be changed, // Algorithm attributes can not be changed,
// list_supported_algo is not supported // list_supported_algo is not supported
return Ok(None); return Ok(None);

View file

@ -30,7 +30,6 @@ use openpgp_card::card_do::{Fingerprint, KeyGenerationTime};
use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial}; use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial};
use openpgp_card::{CardApp, Error, KeyType}; use openpgp_card::{CardApp, Error, KeyType};
use crate::card::Admin;
use crate::privkey::SequoiaKey; use crate::privkey::SequoiaKey;
use crate::signer::CardSigner; use crate::signer::CardSigner;
use crate::{decryptor, signer, PublicKey}; use crate::{decryptor, signer, PublicKey};

View file

@ -71,6 +71,7 @@ impl CardApp {
} }
let (max_cmd_bytes, max_rsp_bytes) = let (max_cmd_bytes, max_rsp_bytes) =
// FIXME: handle cmd/resp limits in ex-cap, for card <3.0 (?)
if let Ok(Some(eli)) = ard.get_extended_length_information() { if let Ok(Some(eli)) = ard.get_extended_length_information() {
(eli.max_command_bytes(), eli.max_response_bytes()) (eli.max_command_bytes(), eli.max_response_bytes())
} else { } else {

View file

@ -4,7 +4,6 @@
//! OpenPGP card data objects (DO) //! OpenPGP card data objects (DO)
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use std::collections::HashSet;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
@ -88,11 +87,18 @@ impl ApplicationRelatedData {
pub fn get_extended_capabilities( pub fn get_extended_capabilities(
&self, &self,
) -> Result<ExtendedCapabilities, Error> { ) -> Result<ExtendedCapabilities, Error> {
// FIXME: caching?
let app_id = self.get_application_id()?;
let version = app_id.version();
// get from cached "application related data" // get from cached "application related data"
let ecap = self.0.find(&[0xc0].into()); let ecap = self.0.find(&[0xc0].into());
if let Some(ecap) = ecap { if let Some(ecap) = ecap {
Ok(ExtendedCapabilities::try_from(&ecap.serialize()[..])?) Ok(ExtendedCapabilities::try_from((
&ecap.serialize()[..],
version,
))?)
} else { } else {
Err(anyhow!("Failed to get extended capabilities.").into()) Err(anyhow!("Failed to get extended capabilities.").into())
} }
@ -236,7 +242,15 @@ pub struct CardServiceData {
/// 4.4.3.7 Extended Capabilities /// 4.4.3.7 Extended Capabilities
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct ExtendedCapabilities { pub struct ExtendedCapabilities {
features: HashSet<ExCapFeatures>, secure_messaging: bool,
get_challenge: bool,
key_import: bool,
pw_status_change: bool,
private_use_dos: bool,
algo_attrs_changeable: bool,
aes: bool,
kdf_do: bool,
sm_algo: u8, sm_algo: u8,
max_len_challenge: u16, max_len_challenge: u16,
max_len_cardholder_cert: u16, max_len_cardholder_cert: u16,
@ -245,19 +259,6 @@ pub struct ExtendedCapabilities {
mse_command_support: bool, mse_command_support: bool,
} }
/// Features (first byte of Extended Capabilities, see 4.4.3.7)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ExCapFeatures {
SecureMessaging,
GetChallenge,
KeyImport,
PwStatusChange,
PrivateUseDOs,
AlgoAttrsChangeable,
Aes,
KdfDo,
}
/// 4.1.3.1 Extended length information /// 4.1.3.1 Extended length information
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct ExtendedLengthInfo { pub struct ExtendedLengthInfo {

View file

@ -4,83 +4,54 @@
//! 4.4.3.7 Extended Capabilities //! 4.4.3.7 Extended Capabilities
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use nom::{combinator, number::complete as number, sequence};
use std::collections::HashSet;
use std::convert::TryFrom; use std::convert::TryFrom;
use crate::card_do::{complete, ExCapFeatures, ExtendedCapabilities}; use crate::card_do::ExtendedCapabilities;
use crate::Error; use crate::Error;
fn features(input: &[u8]) -> nom::IResult<&[u8], HashSet<ExCapFeatures>> {
combinator::map(number::u8, |b| {
let mut f = HashSet::new();
if b & 0x80 != 0 {
f.insert(ExCapFeatures::SecureMessaging);
}
if b & 0x40 != 0 {
f.insert(ExCapFeatures::GetChallenge);
}
if b & 0x20 != 0 {
f.insert(ExCapFeatures::KeyImport);
}
if b & 0x10 != 0 {
f.insert(ExCapFeatures::PwStatusChange);
}
if b & 0x08 != 0 {
f.insert(ExCapFeatures::PrivateUseDOs);
}
if b & 0x04 != 0 {
f.insert(ExCapFeatures::AlgoAttrsChangeable);
}
if b & 0x02 != 0 {
f.insert(ExCapFeatures::Aes);
}
if b & 0x01 != 0 {
f.insert(ExCapFeatures::KdfDo);
}
f
})(input)
}
fn parse(
input: &[u8],
) -> nom::IResult<&[u8], (HashSet<ExCapFeatures>, u8, u16, u16, u16, u8, u8)> {
nom::combinator::all_consuming(sequence::tuple((
features,
number::u8,
number::be_u16,
number::be_u16,
number::be_u16,
number::u8,
number::u8,
)))(input)
}
impl ExtendedCapabilities { impl ExtendedCapabilities {
pub fn features(&self) -> HashSet<ExCapFeatures> {
self.features.clone()
}
pub fn max_len_special_do(&self) -> u16 { pub fn max_len_special_do(&self) -> u16 {
self.max_len_special_do self.max_len_special_do
} }
pub fn algo_attrs_changeable(&self) -> bool {
self.algo_attrs_changeable
}
} }
impl TryFrom<&[u8]> for ExtendedCapabilities { impl TryFrom<(&[u8], u16)> for ExtendedCapabilities {
type Error = Error; type Error = Error;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> { fn try_from((input, version): (&[u8], u16)) -> Result<Self, Self::Error> {
let ( // FIXME: handle different card versions.
features, // e.g. bytes 07/08 and 09/0A have different meanings before and
sm_algo, // after V3.0
max_len_challenge,
max_len_cardholder_cert, assert_eq!(
max_len_special_do, input.len(),
pin_block_2_format_support, 10,
mse_command_support, "extended capabilities with size != 10 are currently unsupported"
) = complete(parse(input))?; );
let b = input[0];
let secure_messaging = b & 0x80 != 0;
let get_challenge = b & 0x40 != 0;
let key_import = b & 0x20 != 0;
let pw_status_change = b & 0x10 != 0;
let private_use_dos = b & 0x08 != 0;
let algo_attrs_changeable = b & 0x04 != 0;
let aes = b & 0x02 != 0;
let kdf_do = b & 0x01 != 0;
let sm_algo = input[1];
let max_len_challenge = input[2] as u16 * 256 + input[3] as u16;
let max_len_cardholder_cert = input[4] as u16 * 256 + input[5] as u16;
let max_len_special_do = input[6] as u16 * 256 + input[7] as u16;
let pin_block_2_format_support = input[8];
let mse_command_support = input[9];
if pin_block_2_format_support > 1 { if pin_block_2_format_support > 1 {
return Err(anyhow!( return Err(anyhow!(
@ -90,17 +61,24 @@ impl TryFrom<&[u8]> for ExtendedCapabilities {
.into()); .into());
} }
// NOTE: yubikey 4 returns 255 for mse_command_support if mse_command_support > 1 {
// if mse_command_support > 1 { return Err(anyhow!(
// return Err(anyhow!( "Illegal value '{}' for mse_command_support",
// "Illegal value '{}' for mse_command_support", mse_command_support
// mse_command_support )
// ) .into());
// .into()); }
// }
Ok(Self { Ok(Self {
features, secure_messaging,
get_challenge,
key_import,
pw_status_change,
private_use_dos,
algo_attrs_changeable,
aes,
kdf_do,
sm_algo, sm_algo,
max_len_challenge, max_len_challenge,
max_len_cardholder_cert, max_len_cardholder_cert,
@ -113,28 +91,27 @@ impl TryFrom<&[u8]> for ExtendedCapabilities {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::card_do::extended_cap::{ExCapFeatures, ExtendedCapabilities}; use crate::card_do::extended_cap::ExtendedCapabilities;
use hex_literal::hex; use hex_literal::hex;
use std::collections::HashSet;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::iter::FromIterator;
#[test] #[test]
fn test_ec() { fn test_ec() {
// Yubikey 5
let data = hex!("7d 00 0b fe 08 00 00 ff 00 00"); let data = hex!("7d 00 0b fe 08 00 00 ff 00 00");
let ec = ExtendedCapabilities::try_from(&data[..]).unwrap(); let ec = ExtendedCapabilities::try_from((&data[..], 0x0304)).unwrap();
assert_eq!( assert_eq!(
ec, ec,
ExtendedCapabilities { ExtendedCapabilities {
features: HashSet::from_iter(vec![ secure_messaging: false,
ExCapFeatures::GetChallenge, get_challenge: true,
ExCapFeatures::KeyImport, key_import: true,
ExCapFeatures::PwStatusChange, pw_status_change: true,
ExCapFeatures::PrivateUseDOs, private_use_dos: true,
ExCapFeatures::AlgoAttrsChangeable, algo_attrs_changeable: true,
ExCapFeatures::KdfDo aes: false,
]), kdf_do: true,
sm_algo: 0x0, sm_algo: 0x0,
max_len_challenge: 0xbfe, max_len_challenge: 0xbfe,
max_len_cardholder_cert: 0x800, max_len_cardholder_cert: 0x800,

View file

@ -11,9 +11,7 @@ use crate::algorithm::{Algo, AlgoInfo, Curve, EccAttrs, RsaAttrs};
use crate::apdu::command::Command; use crate::apdu::command::Command;
use crate::apdu::commands; use crate::apdu::commands;
use crate::card_app::CardApp; use crate::card_app::CardApp;
use crate::card_do::{ use crate::card_do::{ApplicationRelatedData, Fingerprint, KeyGenerationTime};
ApplicationRelatedData, ExCapFeatures, Fingerprint, KeyGenerationTime,
};
use crate::crypto_data::{ use crate::crypto_data::{
CardUploadableKey, EccKey, EccPub, PrivateKeyMaterial, PublicKeyMaterial, CardUploadableKey, EccKey, EccPub, PrivateKeyMaterial, PublicKeyMaterial,
RSAKey, RSAPub, RSAKey, RSAPub,
@ -195,11 +193,7 @@ pub(crate) fn key_import(
// set-operations on the card. // set-operations on the card.
// Only set algo attrs if "Extended Capabilities" lists the feature // Only set algo attrs if "Extended Capabilities" lists the feature
if ard if ard.get_extended_capabilities()?.algo_attrs_changeable() {
.get_extended_capabilities()?
.features()
.contains(&ExCapFeatures::AlgoAttrsChangeable)
{
card_app.set_algorithm_attributes(key_type, &algo)?; card_app.set_algorithm_attributes(key_type, &algo)?;
} }