From 44e915d3e056ce3f992a4207d1a8152e4980b67c Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Sat, 21 Aug 2021 15:59:31 +0200 Subject: [PATCH] Refactor: move OpenPGP card data structures (and parsing) into the module `card_data` --- card-functionality/src/tests.rs | 3 +- openpgp-card-sequoia/src/lib.rs | 13 +- openpgp-card-sequoia/src/main.rs | 4 +- openpgp-card/src/card_app.rs | 153 +------- .../src/{parse => card_data}/algo_attrs.rs | 5 +- .../src/{parse => card_data}/algo_info.rs | 7 +- .../{parse => card_data}/application_id.rs | 4 +- .../src/{parse => card_data}/cardholder.rs | 2 +- .../src/{parse => card_data}/extended_cap.rs | 10 +- .../extended_length_info.rs | 4 +- .../src/{parse => card_data}/fingerprint.rs | 3 +- .../src/{parse => card_data}/historical.rs | 2 +- .../key_generation_times.rs | 4 +- openpgp-card/src/card_data/mod.rs | 363 ++++++++++++++++++ .../src/{parse => card_data}/pw_status.rs | 5 +- openpgp-card/src/lib.rs | 164 +------- openpgp-card/src/parse/mod.rs | 53 --- openpgp-card/src/tlv/mod.rs | 5 +- 18 files changed, 405 insertions(+), 399 deletions(-) rename openpgp-card/src/{parse => card_data}/algo_attrs.rs (98%) rename openpgp-card/src/{parse => card_data}/algo_info.rs (99%) rename openpgp-card/src/{parse => card_data}/application_id.rs (95%) rename openpgp-card/src/{parse => card_data}/cardholder.rs (96%) rename openpgp-card/src/{parse => card_data}/extended_cap.rs (93%) rename openpgp-card/src/{parse => card_data}/extended_length_info.rs (88%) rename openpgp-card/src/{parse => card_data}/fingerprint.rs (97%) rename openpgp-card/src/{parse => card_data}/historical.rs (98%) rename openpgp-card/src/{parse => card_data}/key_generation_times.rs (96%) create mode 100644 openpgp-card/src/card_data/mod.rs rename openpgp-card/src/{parse => card_data}/pw_status.rs (97%) delete mode 100644 openpgp-card/src/parse/mod.rs diff --git a/card-functionality/src/tests.rs b/card-functionality/src/tests.rs index fd35ae3..9201da9 100644 --- a/card-functionality/src/tests.rs +++ b/card-functionality/src/tests.rs @@ -12,8 +12,9 @@ use sequoia_openpgp::types::Timestamp; use sequoia_openpgp::Cert; use openpgp_card::algorithm::AlgoSimple; +use openpgp_card::card_data::Sex; use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError}; -use openpgp_card::{CardApp, KeyType, Sex}; +use openpgp_card::{CardApp, KeyType}; use openpgp_card_sequoia::{ make_cert, public_key_material_to_key, public_to_fingerprint, }; diff --git a/openpgp-card-sequoia/src/lib.rs b/openpgp-card-sequoia/src/lib.rs index 3a1d77e..20d823d 100644 --- a/openpgp-card-sequoia/src/lib.rs +++ b/openpgp-card-sequoia/src/lib.rs @@ -32,12 +32,15 @@ use openpgp::{Cert, Packet}; use sequoia_openpgp as openpgp; use openpgp_card::algorithm::{Algo, AlgoInfo, Curve}; +use openpgp_card::card_data::{ + ApplicationId, ApplicationRelatedData, Cardholder, ExtendedCap, + ExtendedLengthInfo, Features, Fingerprint, Historical, KeySet, PWStatus, + Sex, +}; use openpgp_card::{ - errors::OpenpgpCardError, ApplicationId, ApplicationRelatedData, CardApp, - CardClientBox, CardUploadableKey, Cardholder, DecryptMe, EccKey, EccType, - ExtendedCap, ExtendedLengthInfo, Features, Fingerprint, Hash, Historical, - KeySet, KeyType, PWStatus, PrivateKeyMaterial, PublicKeyMaterial, RSAKey, - Response, Sex, + errors::OpenpgpCardError, CardApp, CardClientBox, CardUploadableKey, + DecryptMe, EccKey, EccType, Hash, KeyType, PrivateKeyMaterial, + PublicKeyMaterial, RSAKey, Response, }; use crate::signer::CardSigner; diff --git a/openpgp-card-sequoia/src/main.rs b/openpgp-card-sequoia/src/main.rs index 09950bb..106a1a6 100644 --- a/openpgp-card-sequoia/src/main.rs +++ b/openpgp-card-sequoia/src/main.rs @@ -8,6 +8,7 @@ use std::error::Error; use sequoia_openpgp::parse::Parse; use sequoia_openpgp::Cert; +use openpgp_card::card_data::Sex; use openpgp_card::{CardApp, KeyType}; use openpgp_card_pcsc::PcscClient; // use openpgp_card_scdc::ScdClient; @@ -108,8 +109,7 @@ fn main() -> Result<(), Box> { let res = oc_admin.set_name("Bar< Result { - // get from cached "application related data" - let aid = self.0.find(&Tag::from([0x4F])); - - if let Some(aid) = aid { - Ok(ApplicationId::try_from(&aid.serialize()[..])?) - } else { - Err(anyhow!("Couldn't get Application ID.").into()) - } - } - - /// Historical bytes - pub fn get_historical(&self) -> Result { - // get from cached "application related data" - let hist = self.0.find(&Tag::from([0x5F, 0x52])); - - if let Some(hist) = hist { - log::debug!("Historical bytes: {:x?}", hist); - Historical::from(&hist.serialize()) - } else { - Err(anyhow!("Failed to get historical bytes.").into()) - } - } - - /// Extended length information (ISO 7816-4) with maximum number of - /// bytes for command and response. - pub fn get_extended_length_information( - &self, - ) -> Result> { - // get from cached "application related data" - let eli = self.0.find(&Tag::from([0x7F, 0x66])); - - log::debug!("Extended length information: {:x?}", eli); - - if let Some(eli) = eli { - // The card has returned extended length information - Ok(Some(ExtendedLengthInfo::from(&eli.serialize()[..])?)) - } else { - // The card didn't return this (optional) DO. That is ok. - Ok(None) - } - } - - pub fn get_general_feature_management() -> Option { - unimplemented!() - } - - pub fn get_discretionary_data_objects() { - unimplemented!() - } - - /// Extended Capabilities - pub fn get_extended_capabilities( - &self, - ) -> Result { - // get from cached "application related data" - let ecap = self.0.find(&Tag::from([0xc0])); - - if let Some(ecap) = ecap { - Ok(ExtendedCap::try_from(&ecap.serialize()[..])?) - } else { - Err(anyhow!("Failed to get extended capabilities.").into()) - } - } - - /// Algorithm attributes (for each key type) - pub fn get_algorithm_attributes(&self, key_type: KeyType) -> Result { - // get from cached "application related data" - let aa = self.0.find(&Tag::from([key_type.get_algorithm_tag()])); - - if let Some(aa) = aa { - Algo::try_from(&aa.serialize()[..]) - } else { - Err(anyhow!( - "Failed to get algorithm attributes for {:?}.", - key_type - )) - } - } - - /// PW status Bytes - pub fn get_pw_status_bytes(&self) -> Result { - // get from cached "application related data" - let psb = self.0.find(&Tag::from([0xc4])); - - if let Some(psb) = psb { - let pws = PWStatus::try_from(&psb.serialize())?; - - log::debug!("PW Status: {:x?}", pws); - - Ok(pws) - } else { - Err(anyhow!("Failed to get PW status Bytes.")) - } - } - - /// Fingerprint, per key type. - /// Zero bytes indicate a not defined private key. - pub fn get_fingerprints( - &self, - ) -> Result, OpenpgpCardError> { - // Get from cached "application related data" - let fp = self.0.find(&Tag::from([0xc5])); - - if let Some(fp) = fp { - let fp = fingerprint::from(&fp.serialize())?; - - log::debug!("Fp: {:x?}", fp); - - Ok(fp) - } else { - Err(anyhow!("Failed to get fingerprints.").into()) - } - } - - /// Generation dates/times of key pairs - pub fn get_key_generation_times( - &self, - ) -> Result, OpenpgpCardError> { - let kg = self.0.find(&Tag::from([0xCD])); - - if let Some(kg) = kg { - let kg = key_generation_times::from(&kg.serialize())?; - - log::debug!("Key generation: {:x?}", kg); - - Ok(kg) - } else { - Err(anyhow!("Failed to get key generation times.").into()) - } - } -} - /// Direct, low-level, access to OpenPGP card functionality. /// /// No checks are performed here (e.g. for valid data lengths). diff --git a/openpgp-card/src/parse/algo_attrs.rs b/openpgp-card/src/card_data/algo_attrs.rs similarity index 98% rename from openpgp-card/src/parse/algo_attrs.rs rename to openpgp-card/src/card_data/algo_attrs.rs index 2f530f3..23db04d 100644 --- a/openpgp-card/src/parse/algo_attrs.rs +++ b/openpgp-card/src/card_data/algo_attrs.rs @@ -10,7 +10,8 @@ use nom::combinator::map; use nom::{branch, bytes::complete as bytes, number::complete as number}; use crate::algorithm::{Algo, Curve, EccAttrs, RsaAttrs}; -use crate::{parse, EccType}; +use crate::card_data::complete; +use crate::EccType; fn parse_oid_cv25519(input: &[u8]) -> nom::IResult<&[u8], Curve> { map(tag(Curve::Cv25519.oid()), |_| Curve::Cv25519)(input) @@ -151,6 +152,6 @@ impl TryFrom<&[u8]> for Algo { type Error = anyhow::Error; fn try_from(data: &[u8]) -> Result { - parse::complete(parse(data)) + complete(parse(data)) } } diff --git a/openpgp-card/src/parse/algo_info.rs b/openpgp-card/src/card_data/algo_info.rs similarity index 99% rename from openpgp-card/src/parse/algo_info.rs rename to openpgp-card/src/card_data/algo_info.rs index a6eed91..4107e53 100644 --- a/openpgp-card/src/parse/algo_info.rs +++ b/openpgp-card/src/card_data/algo_info.rs @@ -10,7 +10,7 @@ use nom::{branch, bytes::complete as bytes, combinator, multi, sequence}; use std::fmt; use crate::algorithm::AlgoInfo; -use crate::parse::algo_attrs; +use crate::card_data::{algo_attrs, complete}; use crate::{Algo, KeyType}; impl AlgoInfo { @@ -91,7 +91,7 @@ impl TryFrom<&[u8]> for AlgoInfo { type Error = anyhow::Error; fn try_from(input: &[u8]) -> Result { - Ok(AlgoInfo(crate::parse::complete(parse(input))?)) + Ok(AlgoInfo(complete(parse(input))?)) } } @@ -101,8 +101,7 @@ impl TryFrom<&[u8]> for AlgoInfo { mod test { use std::convert::TryFrom; - use crate::algorithm::{Algo::*, Curve::*, EccAttrs, RsaAttrs}; - use crate::parse::algo_info::AlgoInfo; + use crate::algorithm::{Algo::*, AlgoInfo, Curve::*, EccAttrs, RsaAttrs}; use crate::{EccType::*, KeyType::*}; #[test] diff --git a/openpgp-card/src/parse/application_id.rs b/openpgp-card/src/card_data/application_id.rs similarity index 95% rename from openpgp-card/src/parse/application_id.rs rename to openpgp-card/src/card_data/application_id.rs index 011d541..8586057 100644 --- a/openpgp-card/src/parse/application_id.rs +++ b/openpgp-card/src/card_data/application_id.rs @@ -5,7 +5,7 @@ use anyhow::Result; use nom::{bytes::complete as bytes, number::complete as number}; use std::convert::TryFrom; -use crate::{parse, ApplicationId}; +use crate::card_data::{complete, ApplicationId}; fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationId> { let (input, _) = bytes::tag([0xd2, 0x76, 0x0, 0x1, 0x24])(input)?; @@ -33,7 +33,7 @@ impl TryFrom<&[u8]> for ApplicationId { type Error = anyhow::Error; fn try_from(data: &[u8]) -> Result { - parse::complete(parse(data)) + complete(parse(data)) } } diff --git a/openpgp-card/src/parse/cardholder.rs b/openpgp-card/src/card_data/cardholder.rs similarity index 96% rename from openpgp-card/src/parse/cardholder.rs rename to openpgp-card/src/card_data/cardholder.rs index 3a5eace..13dab90 100644 --- a/openpgp-card/src/parse/cardholder.rs +++ b/openpgp-card/src/card_data/cardholder.rs @@ -5,9 +5,9 @@ use std::convert::TryFrom; use anyhow::Result; +use crate::card_data::{Cardholder, Sex}; use crate::tlv::tag::Tag; use crate::tlv::{Tlv, TlvEntry}; -use crate::{Cardholder, Sex}; impl TryFrom<&[u8]> for Cardholder { type Error = anyhow::Error; diff --git a/openpgp-card/src/parse/extended_cap.rs b/openpgp-card/src/card_data/extended_cap.rs similarity index 93% rename from openpgp-card/src/parse/extended_cap.rs rename to openpgp-card/src/card_data/extended_cap.rs index 03597c6..4286498 100644 --- a/openpgp-card/src/parse/extended_cap.rs +++ b/openpgp-card/src/card_data/extended_cap.rs @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::errors::OpenpgpCardError; -use crate::parse; use anyhow::Result; use nom::{combinator, number::complete as number, sequence}; use std::collections::HashSet; use std::convert::TryFrom; -use crate::{ExtendedCap, Features}; +use crate::card_data::complete; +use crate::card_data::{ExtendedCap, Features}; +use crate::errors::OpenpgpCardError; fn features(input: &[u8]) -> nom::IResult<&[u8], HashSet> { combinator::map(number::u8, |b| { @@ -61,7 +61,7 @@ impl TryFrom<&[u8]> for ExtendedCap { type Error = OpenpgpCardError; fn try_from(input: &[u8]) -> Result { - let ec = parse::complete(parse(input))?; + let ec = complete(parse(input))?; Ok(Self { features: ec.0, @@ -77,7 +77,7 @@ impl TryFrom<&[u8]> for ExtendedCap { #[cfg(test)] mod test { - use crate::parse::extended_cap::{ExtendedCap, Features}; + use crate::card_data::extended_cap::{ExtendedCap, Features}; use hex_literal::hex; use std::collections::HashSet; use std::convert::TryFrom; diff --git a/openpgp-card/src/parse/extended_length_info.rs b/openpgp-card/src/card_data/extended_length_info.rs similarity index 88% rename from openpgp-card/src/parse/extended_length_info.rs rename to openpgp-card/src/card_data/extended_length_info.rs index 21b778e..8cbe7f1 100644 --- a/openpgp-card/src/parse/extended_length_info.rs +++ b/openpgp-card/src/card_data/extended_length_info.rs @@ -4,7 +4,7 @@ use anyhow::Result; use nom::{bytes::complete::tag, number::complete as number, sequence}; -use crate::{parse, ExtendedLengthInfo}; +use crate::card_data::{complete, ExtendedLengthInfo}; fn parse(input: &[u8]) -> nom::IResult<&[u8], (u16, u16)> { let (input, (_, cmd, _, resp)) = @@ -20,7 +20,7 @@ fn parse(input: &[u8]) -> nom::IResult<&[u8], (u16, u16)> { impl ExtendedLengthInfo { pub fn from(input: &[u8]) -> Result { - let eli = parse::complete(parse(input))?; + let eli = complete(parse(input))?; Ok(Self { max_command_bytes: eli.0, diff --git a/openpgp-card/src/parse/fingerprint.rs b/openpgp-card/src/card_data/fingerprint.rs similarity index 97% rename from openpgp-card/src/parse/fingerprint.rs rename to openpgp-card/src/card_data/fingerprint.rs index 8093bd9..09bf3a2 100644 --- a/openpgp-card/src/parse/fingerprint.rs +++ b/openpgp-card/src/card_data/fingerprint.rs @@ -5,9 +5,8 @@ use anyhow::anyhow; use nom::{bytes::complete as bytes, combinator, sequence}; use std::fmt; +use crate::card_data::{Fingerprint, KeySet}; use crate::errors::OpenpgpCardError; -use crate::parse::KeySet; -use crate::Fingerprint; impl From<[u8; 20]> for Fingerprint { fn from(data: [u8; 20]) -> Self { diff --git a/openpgp-card/src/parse/historical.rs b/openpgp-card/src/card_data/historical.rs similarity index 98% rename from openpgp-card/src/parse/historical.rs rename to openpgp-card/src/card_data/historical.rs index d83cce5..be078db 100644 --- a/openpgp-card/src/parse/historical.rs +++ b/openpgp-card/src/card_data/historical.rs @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::card_data::{CardCapabilities, CardServiceData, Historical}; use crate::errors::OpenpgpCardError; -use crate::{CardCapabilities, CardServiceData, Historical}; use anyhow::{anyhow, Result}; impl CardCapabilities { diff --git a/openpgp-card/src/parse/key_generation_times.rs b/openpgp-card/src/card_data/key_generation_times.rs similarity index 96% rename from openpgp-card/src/parse/key_generation_times.rs rename to openpgp-card/src/card_data/key_generation_times.rs index b8c1a97..d22669c 100644 --- a/openpgp-card/src/parse/key_generation_times.rs +++ b/openpgp-card/src/card_data/key_generation_times.rs @@ -5,9 +5,9 @@ use anyhow::anyhow; use chrono::{DateTime, NaiveDateTime, Utc}; use nom::{combinator, number::complete as number, sequence}; +use crate::card_data::KeyGeneration; +use crate::card_data::KeySet; use crate::errors::OpenpgpCardError; -use crate::parse::KeySet; -use crate::KeyGeneration; impl From for DateTime { fn from(kg: KeyGeneration) -> Self { diff --git a/openpgp-card/src/card_data/mod.rs b/openpgp-card/src/card_data/mod.rs new file mode 100644 index 0000000..e133aa0 --- /dev/null +++ b/openpgp-card/src/card_data/mod.rs @@ -0,0 +1,363 @@ +// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! Data structures for OpenPGP card data + +use anyhow::{anyhow, Error, Result}; +use std::collections::HashSet; +use std::convert::TryFrom; + +use crate::algorithm::Algo; +use crate::errors::OpenpgpCardError; +use crate::tlv::{tag::Tag, Tlv}; +use crate::KeyType; + +mod algo_attrs; +mod algo_info; +mod application_id; +mod cardholder; +mod extended_cap; +mod extended_length_info; +mod fingerprint; +mod historical; +mod key_generation_times; +mod pw_status; + +/// Application Related Data +/// +/// The "application related data" DO contains a set of DOs. +/// This struct offers read access to these DOs. +/// +/// Note that when any of the information in this DO changes on the card, you +/// need to read ApplicationRelatedData from the card again to receive the +/// current values. +pub struct ApplicationRelatedData(pub(crate) Tlv); + +impl ApplicationRelatedData { + /// Application identifier (AID), ISO 7816-4 + pub fn get_aid(&self) -> Result { + // get from cached "application related data" + let aid = self.0.find(&Tag::from([0x4F])); + + if let Some(aid) = aid { + Ok(ApplicationId::try_from(&aid.serialize()[..])?) + } else { + Err(anyhow!("Couldn't get Application ID.").into()) + } + } + + /// Historical bytes + pub fn get_historical(&self) -> Result { + // get from cached "application related data" + let hist = self.0.find(&Tag::from([0x5F, 0x52])); + + if let Some(hist) = hist { + log::debug!("Historical bytes: {:x?}", hist); + Historical::from(&hist.serialize()) + } else { + Err(anyhow!("Failed to get historical bytes.").into()) + } + } + + /// Extended length information (ISO 7816-4) with maximum number of + /// bytes for command and response. + pub fn get_extended_length_information( + &self, + ) -> Result> { + // get from cached "application related data" + let eli = self.0.find(&Tag::from([0x7F, 0x66])); + + log::debug!("Extended length information: {:x?}", eli); + + if let Some(eli) = eli { + // The card has returned extended length information + Ok(Some(ExtendedLengthInfo::from(&eli.serialize()[..])?)) + } else { + // The card didn't return this (optional) DO. That is ok. + Ok(None) + } + } + + pub fn get_general_feature_management() -> Option { + unimplemented!() + } + + pub fn get_discretionary_data_objects() { + unimplemented!() + } + + /// Extended Capabilities + pub fn get_extended_capabilities( + &self, + ) -> Result { + // get from cached "application related data" + let ecap = self.0.find(&Tag::from([0xc0])); + + if let Some(ecap) = ecap { + Ok(ExtendedCap::try_from(&ecap.serialize()[..])?) + } else { + Err(anyhow!("Failed to get extended capabilities.").into()) + } + } + + /// Algorithm attributes (for each key type) + pub fn get_algorithm_attributes(&self, key_type: KeyType) -> Result { + // get from cached "application related data" + let aa = self.0.find(&Tag::from([key_type.get_algorithm_tag()])); + + if let Some(aa) = aa { + Algo::try_from(&aa.serialize()[..]) + } else { + Err(anyhow!( + "Failed to get algorithm attributes for {:?}.", + key_type + )) + } + } + + /// PW status Bytes + pub fn get_pw_status_bytes(&self) -> Result { + // get from cached "application related data" + let psb = self.0.find(&Tag::from([0xc4])); + + if let Some(psb) = psb { + let pws = PWStatus::try_from(&psb.serialize())?; + + log::debug!("PW Status: {:x?}", pws); + + Ok(pws) + } else { + Err(anyhow!("Failed to get PW status Bytes.")) + } + } + + /// Fingerprint, per key type. + /// Zero bytes indicate a not defined private key. + pub fn get_fingerprints( + &self, + ) -> Result, OpenpgpCardError> { + // Get from cached "application related data" + let fp = self.0.find(&Tag::from([0xc5])); + + if let Some(fp) = fp { + let fp = fingerprint::from(&fp.serialize())?; + + log::debug!("Fp: {:x?}", fp); + + Ok(fp) + } else { + Err(anyhow!("Failed to get fingerprints.").into()) + } + } + + /// Generation dates/times of key pairs + pub fn get_key_generation_times( + &self, + ) -> Result, OpenpgpCardError> { + let kg = self.0.find(&Tag::from([0xCD])); + + if let Some(kg) = kg { + let kg = key_generation_times::from(&kg.serialize())?; + + log::debug!("Key generation: {:x?}", kg); + + Ok(kg) + } else { + Err(anyhow!("Failed to get key generation times.").into()) + } + } +} + +/// An OpenPGP key generation Time +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct KeyGeneration(u32); + +impl KeyGeneration { + pub fn get(&self) -> u32 { + self.0 + } +} + +/// Application identifier (AID) +#[derive(Debug, Eq, PartialEq)] +pub struct ApplicationId { + pub application: u8, + + // GnuPG says: + // if (app->appversion >= 0x0200) + // app->app_local->extcap.is_v2 = 1; + // + // if (app->appversion >= 0x0300) + // app->app_local->extcap.is_v3 = 1; + pub version: u16, + + pub manufacturer: u16, + + pub serial: u32, +} + +/// Card Capabilities (73) +#[derive(Debug)] +pub struct CardCapabilities { + command_chaining: bool, + extended_lc_le: bool, + extended_length_information: bool, +} + +/// Card service data (31) +#[derive(Debug)] +pub struct CardServiceData { + select_by_full_df_name: bool, + select_by_partial_df_name: bool, + dos_available_in_ef_dir: bool, + dos_available_in_ef_atr_info: bool, + access_services: [bool; 3], + mf: bool, +} + +/// Historical Bytes +#[derive(Debug)] +pub struct Historical { + /// category indicator byte + cib: u8, + + /// Card service data (31) + csd: Option, + + /// Card Capabilities (73) + cc: Option, + + /// status indicator byte (o-card 3.4.1, pg 44) + sib: u8, +} + +/// Extended Capabilities +#[derive(Debug, Eq, PartialEq)] +pub struct ExtendedCap { + pub features: HashSet, + sm: u8, + max_len_challenge: u16, + max_len_cardholder_cert: u16, + pub max_len_special_do: u16, + pin_2_format: bool, + mse_command: bool, +} + +/// Features (first byte of Extended Capabilities) +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum Features { + SecureMessaging, + GetChallenge, + KeyImport, + PwStatusChange, + PrivateUseDOs, + AlgoAttrsChangeable, + Aes, + KdfDo, +} + +/// Extended length information +#[derive(Debug, Eq, PartialEq)] +pub struct ExtendedLengthInfo { + pub max_command_bytes: u16, + pub max_response_bytes: u16, +} + +/// Cardholder Related Data +#[derive(Debug)] +pub struct Cardholder { + pub name: Option, + pub lang: Option>, + pub sex: Option, +} + +/// Sex (according to ISO 5218) +#[derive(Debug, PartialEq)] +pub enum Sex { + NotKnown, + Male, + Female, + NotApplicable, +} + +impl From<&Sex> for u8 { + fn from(sex: &Sex) -> u8 { + match sex { + Sex::NotKnown => 0x30, + Sex::Male => 0x31, + Sex::Female => 0x32, + Sex::NotApplicable => 0x39, + } + } +} + +impl From for Sex { + fn from(s: u8) -> Self { + match s { + 0x31 => Sex::Male, + 0x32 => Sex::Female, + 0x39 => Sex::NotApplicable, + _ => Sex::NotKnown, + } + } +} + +/// PW status Bytes +#[derive(Debug)] +pub struct PWStatus { + pub(crate) pw1_cds_multi: bool, + pub(crate) pw1_derived: bool, + pub(crate) pw1_len: u8, + pub(crate) rc_len: u8, + pub(crate) pw3_derived: bool, + pub(crate) pw3_len: u8, + pub(crate) err_count_pw1: u8, + pub(crate) err_count_rst: u8, + pub(crate) err_count_pw3: u8, +} + +/// Fingerprint +#[derive(Clone, Eq, PartialEq)] +pub struct Fingerprint([u8; 20]); + +/// A KeySet binds together a triple of information about each Key on a card +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct KeySet { + signature: Option, + decryption: Option, + authentication: Option, +} + +impl From<(Option, Option, Option)> for KeySet { + fn from(tuple: (Option, Option, Option)) -> Self { + Self { + signature: tuple.0, + decryption: tuple.1, + authentication: tuple.2, + } + } +} + +impl KeySet { + pub fn signature(&self) -> Option<&T> { + self.signature.as_ref() + } + + pub fn decryption(&self) -> Option<&T> { + self.decryption.as_ref() + } + + pub fn authentication(&self) -> Option<&T> { + self.authentication.as_ref() + } +} + +pub(crate) fn complete(result: nom::IResult<&[u8], O>) -> Result { + let (rem, output) = + result.map_err(|err| anyhow!("Parsing failed: {:?}", err))?; + if rem.is_empty() { + Ok(output) + } else { + Err(anyhow!("Parsing incomplete -- trailing data: {:x?}", rem)) + } +} diff --git a/openpgp-card/src/parse/pw_status.rs b/openpgp-card/src/card_data/pw_status.rs similarity index 97% rename from openpgp-card/src/parse/pw_status.rs rename to openpgp-card/src/card_data/pw_status.rs index a5478ef..9e13526 100644 --- a/openpgp-card/src/parse/pw_status.rs +++ b/openpgp-card/src/card_data/pw_status.rs @@ -1,10 +1,11 @@ // SPDX-FileCopyrightText: 2021 Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::errors::OpenpgpCardError; -use crate::PWStatus; use anyhow::anyhow; +use crate::card_data::PWStatus; +use crate::errors::OpenpgpCardError; + impl PWStatus { pub fn try_from(input: &[u8]) -> Result { if input.len() == 7 { diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index b5f5322..d6068b9 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -24,20 +24,18 @@ //! [Sequoia PGP](https://sequoia-pgp.org/) implementation. use anyhow::Result; -use std::collections::HashSet; use crate::algorithm::Algo; pub mod algorithm; mod apdu; mod card_app; +pub mod card_data; pub mod errors; mod keys; -mod parse; mod tlv; pub use crate::apdu::response::Response; -pub use crate::card_app::ApplicationRelatedData; pub use crate::card_app::CardApp; /// The CardClient trait defines communication with an OpenPGP card via a @@ -109,16 +107,6 @@ impl CardCaps { } } -/// An OpenPGP key generation Time -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct KeyGeneration(u32); - -impl KeyGeneration { - pub fn get(&self) -> u32 { - self.0 - } -} - /// Container for a hash value. /// These hash values can be signed by the card. pub enum Hash<'a> { @@ -239,156 +227,6 @@ pub enum DecryptMe<'a> { // ---------- -/// Application identifier (AID) -#[derive(Debug, Eq, PartialEq)] -pub struct ApplicationId { - pub application: u8, - - // GnuPG says: - // if (app->appversion >= 0x0200) - // app->app_local->extcap.is_v2 = 1; - // - // if (app->appversion >= 0x0300) - // app->app_local->extcap.is_v3 = 1; - pub version: u16, - - pub manufacturer: u16, - - pub serial: u32, -} - -/// Card Capabilities (73) -#[derive(Debug)] -pub struct CardCapabilities { - command_chaining: bool, - extended_lc_le: bool, - extended_length_information: bool, -} - -/// Card service data (31) -#[derive(Debug)] -pub struct CardServiceData { - select_by_full_df_name: bool, - select_by_partial_df_name: bool, - dos_available_in_ef_dir: bool, - dos_available_in_ef_atr_info: bool, - access_services: [bool; 3], - mf: bool, -} - -/// Historical Bytes -#[derive(Debug)] -pub struct Historical { - /// category indicator byte - cib: u8, - - /// Card service data (31) - csd: Option, - - /// Card Capabilities (73) - cc: Option, - - /// status indicator byte (o-card 3.4.1, pg 44) - sib: u8, -} - -/// Extended Capabilities -#[derive(Debug, Eq, PartialEq)] -pub struct ExtendedCap { - pub features: HashSet, - sm: u8, - max_len_challenge: u16, - max_len_cardholder_cert: u16, - pub max_len_special_do: u16, - pin_2_format: bool, - mse_command: bool, -} - -/// Features (first byte of Extended Capabilities) -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum Features { - SecureMessaging, - GetChallenge, - KeyImport, - PwStatusChange, - PrivateUseDOs, - AlgoAttrsChangeable, - Aes, - KdfDo, -} - -/// Extended length information -#[derive(Debug, Eq, PartialEq)] -pub struct ExtendedLengthInfo { - pub max_command_bytes: u16, - pub max_response_bytes: u16, -} - -/// Cardholder Related Data -#[derive(Debug)] -pub struct Cardholder { - pub name: Option, - pub lang: Option>, - pub sex: Option, -} - -/// Sex (according to ISO 5218) -#[derive(Debug, PartialEq)] -pub enum Sex { - NotKnown, - Male, - Female, - NotApplicable, -} - -impl From<&Sex> for u8 { - fn from(sex: &Sex) -> u8 { - match sex { - Sex::NotKnown => 0x30, - Sex::Male => 0x31, - Sex::Female => 0x32, - Sex::NotApplicable => 0x39, - } - } -} - -impl From for Sex { - fn from(s: u8) -> Self { - match s { - 0x31 => Sex::Male, - 0x32 => Sex::Female, - 0x39 => Sex::NotApplicable, - _ => Sex::NotKnown, - } - } -} - -/// PW status Bytes -#[derive(Debug)] -pub struct PWStatus { - pub(crate) pw1_cds_multi: bool, - pub(crate) pw1_derived: bool, - pub(crate) pw1_len: u8, - pub(crate) rc_len: u8, - pub(crate) pw3_derived: bool, - pub(crate) pw3_len: u8, - pub(crate) err_count_pw1: u8, - pub(crate) err_count_rst: u8, - pub(crate) err_count_pw3: u8, -} - -/// Fingerprint -#[derive(Clone, Eq, PartialEq)] -pub struct Fingerprint([u8; 20]); - -/// A KeySet binds together a triple of information about each Key on a card -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct KeySet { - signature: Option, - decryption: Option, - authentication: Option, -} - /// Enum to identify the Key-slots on an OpenPGP card #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum KeyType { diff --git a/openpgp-card/src/parse/mod.rs b/openpgp-card/src/parse/mod.rs deleted file mode 100644 index 2cb6e21..0000000 --- a/openpgp-card/src/parse/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer -// SPDX-License-Identifier: MIT OR Apache-2.0 - -//! Parsing of replies to GET DO requests. -//! Turn OpenPGP card replies into our own data structures. - -pub mod algo_attrs; -pub mod algo_info; -pub mod application_id; -pub mod cardholder; -pub mod extended_cap; -pub mod extended_length_info; -pub mod fingerprint; -pub mod historical; -pub mod key_generation_times; -pub mod pw_status; - -use crate::KeySet; -use anyhow::{anyhow, Error}; - -impl From<(Option, Option, Option)> for KeySet { - fn from(tuple: (Option, Option, Option)) -> Self { - Self { - signature: tuple.0, - decryption: tuple.1, - authentication: tuple.2, - } - } -} - -impl KeySet { - pub fn signature(&self) -> Option<&T> { - self.signature.as_ref() - } - - pub fn decryption(&self) -> Option<&T> { - self.decryption.as_ref() - } - - pub fn authentication(&self) -> Option<&T> { - self.authentication.as_ref() - } -} - -pub fn complete(result: nom::IResult<&[u8], O>) -> Result { - let (rem, output) = - result.map_err(|err| anyhow!("Parsing failed: {:?}", err))?; - if rem.is_empty() { - Ok(output) - } else { - Err(anyhow!("Parsing incomplete -- trailing data: {:x?}", rem)) - } -} diff --git a/openpgp-card/src/tlv/mod.rs b/openpgp-card/src/tlv/mod.rs index 9ef2c47..0bd40a4 100644 --- a/openpgp-card/src/tlv/mod.rs +++ b/openpgp-card/src/tlv/mod.rs @@ -8,6 +8,7 @@ use nom::{bytes::complete as bytes, combinator}; pub mod length; pub mod tag; +use crate::card_data::complete; use tag::Tag; #[derive(Debug, Eq, PartialEq)] @@ -54,7 +55,7 @@ impl Tlv { } pub fn try_from(input: &[u8]) -> Result { - crate::parse::complete(Tlv::parse(input)) + complete(Tlv::parse(input)) } } @@ -94,7 +95,7 @@ impl TlvEntry { } pub fn from(data: &[u8], constructed: bool) -> Result { - crate::parse::complete(Self::parse(data, constructed)) + complete(Self::parse(data, constructed)) } pub fn serialize(&self) -> Vec {