extended capabilities: restructure, to prepare for different versions of this DO
This commit is contained in:
parent
245740febc
commit
f2e5fea0fc
6 changed files with 86 additions and 116 deletions
|
@ -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);
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue