From f2e5fea0fcb6f8081a6030f7cb28e85896e9dbbb Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Tue, 14 Sep 2021 01:50:56 +0200 Subject: [PATCH] extended capabilities: restructure, to prepare for different versions of this DO --- openpgp-card-sequoia/src/card.rs | 8 +- openpgp-card-sequoia/src/util.rs | 1 - openpgp-card/src/card_app.rs | 1 + openpgp-card/src/card_do.rs | 33 ++--- openpgp-card/src/card_do/extended_cap.rs | 149 ++++++++++------------- openpgp-card/src/keys.rs | 10 +- 6 files changed, 86 insertions(+), 116 deletions(-) diff --git a/openpgp-card-sequoia/src/card.rs b/openpgp-card-sequoia/src/card.rs index ea9fd1a..7b1f435 100644 --- a/openpgp-card-sequoia/src/card.rs +++ b/openpgp-card-sequoia/src/card.rs @@ -14,11 +14,9 @@ use sequoia_openpgp::Cert; use openpgp_card::algorithm::{Algo, AlgoInfo}; use openpgp_card::card_do::{ ApplicationIdentifier, ApplicationRelatedData, CardholderRelatedData, - ExCapFeatures, ExtendedCapabilities, ExtendedLengthInfo, Fingerprint, - HistoricalBytes, KeyGenerationTime, PWStatusBytes, - SecuritySupportTemplate, Sex, + ExtendedCapabilities, ExtendedLengthInfo, Fingerprint, HistoricalBytes, + KeyGenerationTime, PWStatusBytes, SecuritySupportTemplate, Sex, }; -use openpgp_card::crypto_data::CardUploadableKey; use openpgp_card::{CardApp, CardClientBox, Error, KeySet, KeyType, Response}; use crate::decryptor::CardDecryptor; @@ -245,7 +243,7 @@ impl Open { // The DO "Algorithm Information" (Tag FA) shall be present if // Algorithm attributes can be changed let ec = self.extended_capabilities()?; - if !ec.features().contains(&ExCapFeatures::AlgoAttrsChangeable) { + if !ec.algo_attrs_changeable() { // Algorithm attributes can not be changed, // list_supported_algo is not supported return Ok(None); diff --git a/openpgp-card-sequoia/src/util.rs b/openpgp-card-sequoia/src/util.rs index 25349f5..b4a831d 100644 --- a/openpgp-card-sequoia/src/util.rs +++ b/openpgp-card-sequoia/src/util.rs @@ -30,7 +30,6 @@ use openpgp_card::card_do::{Fingerprint, KeyGenerationTime}; use openpgp_card::crypto_data::{CardUploadableKey, PublicKeyMaterial}; use openpgp_card::{CardApp, Error, KeyType}; -use crate::card::Admin; use crate::privkey::SequoiaKey; use crate::signer::CardSigner; use crate::{decryptor, signer, PublicKey}; diff --git a/openpgp-card/src/card_app.rs b/openpgp-card/src/card_app.rs index 63d4363..7ab8168 100644 --- a/openpgp-card/src/card_app.rs +++ b/openpgp-card/src/card_app.rs @@ -71,6 +71,7 @@ impl CardApp { } 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() { (eli.max_command_bytes(), eli.max_response_bytes()) } else { diff --git a/openpgp-card/src/card_do.rs b/openpgp-card/src/card_do.rs index 6401b8f..9543b7e 100644 --- a/openpgp-card/src/card_do.rs +++ b/openpgp-card/src/card_do.rs @@ -4,7 +4,6 @@ //! OpenPGP card data objects (DO) use anyhow::{anyhow, Result}; -use std::collections::HashSet; use std::convert::TryFrom; use std::convert::TryInto; @@ -88,11 +87,18 @@ impl ApplicationRelatedData { pub fn get_extended_capabilities( &self, ) -> Result { + // FIXME: caching? + let app_id = self.get_application_id()?; + let version = app_id.version(); + // get from cached "application related data" let ecap = self.0.find(&[0xc0].into()); if let Some(ecap) = ecap { - Ok(ExtendedCapabilities::try_from(&ecap.serialize()[..])?) + Ok(ExtendedCapabilities::try_from(( + &ecap.serialize()[..], + version, + ))?) } else { Err(anyhow!("Failed to get extended capabilities.").into()) } @@ -236,7 +242,15 @@ pub struct CardServiceData { /// 4.4.3.7 Extended Capabilities #[derive(Debug, Eq, PartialEq)] pub struct ExtendedCapabilities { - features: HashSet, + 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, max_len_challenge: u16, max_len_cardholder_cert: u16, @@ -245,19 +259,6 @@ pub struct ExtendedCapabilities { 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 #[derive(Debug, Eq, PartialEq)] pub struct ExtendedLengthInfo { diff --git a/openpgp-card/src/card_do/extended_cap.rs b/openpgp-card/src/card_do/extended_cap.rs index f628c53..79ad0ac 100644 --- a/openpgp-card/src/card_do/extended_cap.rs +++ b/openpgp-card/src/card_do/extended_cap.rs @@ -4,83 +4,54 @@ //! 4.4.3.7 Extended Capabilities use anyhow::{anyhow, Result}; -use nom::{combinator, number::complete as number, sequence}; -use std::collections::HashSet; use std::convert::TryFrom; -use crate::card_do::{complete, ExCapFeatures, ExtendedCapabilities}; +use crate::card_do::ExtendedCapabilities; use crate::Error; -fn features(input: &[u8]) -> nom::IResult<&[u8], HashSet> { - 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, 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 { - pub fn features(&self) -> HashSet { - self.features.clone() - } - pub fn max_len_special_do(&self) -> u16 { 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; - fn try_from(input: &[u8]) -> Result { - let ( - features, - sm_algo, - max_len_challenge, - max_len_cardholder_cert, - max_len_special_do, - pin_block_2_format_support, - mse_command_support, - ) = complete(parse(input))?; + fn try_from((input, version): (&[u8], u16)) -> Result { + // FIXME: handle different card versions. + // e.g. bytes 07/08 and 09/0A have different meanings before and + // after V3.0 + + assert_eq!( + input.len(), + 10, + "extended capabilities with size != 10 are currently unsupported" + ); + + 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 { return Err(anyhow!( @@ -90,17 +61,24 @@ impl TryFrom<&[u8]> for ExtendedCapabilities { .into()); } - // NOTE: yubikey 4 returns 255 for mse_command_support - // if mse_command_support > 1 { - // return Err(anyhow!( - // "Illegal value '{}' for mse_command_support", - // mse_command_support - // ) - // .into()); - // } + if mse_command_support > 1 { + return Err(anyhow!( + "Illegal value '{}' for mse_command_support", + mse_command_support + ) + .into()); + } Ok(Self { - features, + secure_messaging, + get_challenge, + key_import, + pw_status_change, + private_use_dos, + algo_attrs_changeable, + aes, + kdf_do, + sm_algo, max_len_challenge, max_len_cardholder_cert, @@ -113,28 +91,27 @@ impl TryFrom<&[u8]> for ExtendedCapabilities { #[cfg(test)] mod test { - use crate::card_do::extended_cap::{ExCapFeatures, ExtendedCapabilities}; + use crate::card_do::extended_cap::ExtendedCapabilities; use hex_literal::hex; - use std::collections::HashSet; use std::convert::TryFrom; - use std::iter::FromIterator; #[test] fn test_ec() { + // Yubikey 5 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!( ec, ExtendedCapabilities { - features: HashSet::from_iter(vec![ - ExCapFeatures::GetChallenge, - ExCapFeatures::KeyImport, - ExCapFeatures::PwStatusChange, - ExCapFeatures::PrivateUseDOs, - ExCapFeatures::AlgoAttrsChangeable, - ExCapFeatures::KdfDo - ]), + secure_messaging: false, + get_challenge: true, + key_import: true, + pw_status_change: true, + private_use_dos: true, + algo_attrs_changeable: true, + aes: false, + kdf_do: true, sm_algo: 0x0, max_len_challenge: 0xbfe, max_len_cardholder_cert: 0x800, diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 3933e6a..b43f6fd 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -11,9 +11,7 @@ use crate::algorithm::{Algo, AlgoInfo, Curve, EccAttrs, RsaAttrs}; use crate::apdu::command::Command; use crate::apdu::commands; use crate::card_app::CardApp; -use crate::card_do::{ - ApplicationRelatedData, ExCapFeatures, Fingerprint, KeyGenerationTime, -}; +use crate::card_do::{ApplicationRelatedData, Fingerprint, KeyGenerationTime}; use crate::crypto_data::{ CardUploadableKey, EccKey, EccPub, PrivateKeyMaterial, PublicKeyMaterial, RSAKey, RSAPub, @@ -195,11 +193,7 @@ pub(crate) fn key_import( // set-operations on the card. // Only set algo attrs if "Extended Capabilities" lists the feature - if ard - .get_extended_capabilities()? - .features() - .contains(&ExCapFeatures::AlgoAttrsChangeable) - { + if ard.get_extended_capabilities()?.algo_attrs_changeable() { card_app.set_algorithm_attributes(key_type, &algo)?; }