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::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);

View file

@ -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};

View file

@ -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 {

View file

@ -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<ExtendedCapabilities, Error> {
// 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<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,
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 {

View file

@ -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<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 {
pub fn features(&self) -> HashSet<ExCapFeatures> {
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<Self, Self::Error> {
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<Self, Self::Error> {
// 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,

View file

@ -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)?;
}