Refactor: move OpenPGP card data structures (and parsing) into the module card_data

This commit is contained in:
Heiko Schaefer 2021-08-21 15:59:31 +02:00
parent 0e0602f3d5
commit 44e915d3e0
18 changed files with 405 additions and 399 deletions

View file

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

View file

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

View file

@ -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<dyn Error>> {
let res = oc_admin.set_name("Bar<<Foo")?;
println!("set name {:x?}", res);
let res =
oc_admin.set_sex(openpgp_card::Sex::NotApplicable)?;
let res = oc_admin.set_sex(Sex::NotApplicable)?;
println!("set sex {:x?}", res);
let res = oc_admin.set_lang("en")?;

View file

@ -10,161 +10,14 @@ use anyhow::{anyhow, Result};
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple, RsaAttrs};
use crate::apdu::{commands, response::Response};
use crate::card_data::{ApplicationRelatedData, Cardholder, Sex};
use crate::errors::OpenpgpCardError;
use crate::parse::{fingerprint, key_generation_times};
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
use crate::{
apdu, keys, ApplicationId, CardCaps, CardClientBox, CardUploadableKey,
Cardholder, DecryptMe, EccType, ExtendedCap, ExtendedLengthInfo,
Fingerprint, Hash, Historical, KeyGeneration, KeySet, KeyType, PWStatus,
PublicKeyMaterial, Sex,
apdu, keys, CardCaps, CardClientBox, CardUploadableKey, DecryptMe,
EccType, Hash, KeyType, PublicKeyMaterial,
};
/// 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(Tlv);
impl ApplicationRelatedData {
/// Application identifier (AID), ISO 7816-4
pub fn get_aid(&self) -> Result<ApplicationId, OpenpgpCardError> {
// 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<Historical, OpenpgpCardError> {
// 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<Option<ExtendedLengthInfo>> {
// 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<bool> {
unimplemented!()
}
pub fn get_discretionary_data_objects() {
unimplemented!()
}
/// Extended Capabilities
pub fn get_extended_capabilities(
&self,
) -> Result<ExtendedCap, OpenpgpCardError> {
// 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<Algo> {
// 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<PWStatus> {
// 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<KeySet<Fingerprint>, 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<KeySet<KeyGeneration>, 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).

View file

@ -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<Self> {
parse::complete(parse(data))
complete(parse(data))
}
}

View file

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

View file

@ -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<Self> {
parse::complete(parse(data))
complete(parse(data))
}
}

View file

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

View file

@ -1,14 +1,14 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// 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<Features>> {
combinator::map(number::u8, |b| {
@ -61,7 +61,7 @@ impl TryFrom<&[u8]> for ExtendedCap {
type Error = OpenpgpCardError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
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;

View file

@ -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<Self> {
let eli = parse::complete(parse(input))?;
let eli = complete(parse(input))?;
Ok(Self {
max_command_bytes: eli.0,

View file

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

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// 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 {

View file

@ -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<KeyGeneration> for DateTime<Utc> {
fn from(kg: KeyGeneration) -> Self {

View file

@ -0,0 +1,363 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// 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<ApplicationId, OpenpgpCardError> {
// 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<Historical, OpenpgpCardError> {
// 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<Option<ExtendedLengthInfo>> {
// 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<bool> {
unimplemented!()
}
pub fn get_discretionary_data_objects() {
unimplemented!()
}
/// Extended Capabilities
pub fn get_extended_capabilities(
&self,
) -> Result<ExtendedCap, OpenpgpCardError> {
// 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<Algo> {
// 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<PWStatus> {
// 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<KeySet<Fingerprint>, 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<KeySet<KeyGeneration>, 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<CardServiceData>,
/// Card Capabilities (73)
cc: Option<CardCapabilities>,
/// status indicator byte (o-card 3.4.1, pg 44)
sib: u8,
}
/// Extended Capabilities
#[derive(Debug, Eq, PartialEq)]
pub struct ExtendedCap {
pub features: HashSet<Features>,
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<String>,
pub lang: Option<Vec<[char; 2]>>,
pub sex: Option<Sex>,
}
/// 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<u8> 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<T> {
signature: Option<T>,
decryption: Option<T>,
authentication: Option<T>,
}
impl<T> From<(Option<T>, Option<T>, Option<T>)> for KeySet<T> {
fn from(tuple: (Option<T>, Option<T>, Option<T>)) -> Self {
Self {
signature: tuple.0,
decryption: tuple.1,
authentication: tuple.2,
}
}
}
impl<T> KeySet<T> {
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<O>(result: nom::IResult<&[u8], O>) -> Result<O, Error> {
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))
}
}

View file

@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// 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<Self, OpenpgpCardError> {
if input.len() == 7 {

View file

@ -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<CardServiceData>,
/// Card Capabilities (73)
cc: Option<CardCapabilities>,
/// status indicator byte (o-card 3.4.1, pg 44)
sib: u8,
}
/// Extended Capabilities
#[derive(Debug, Eq, PartialEq)]
pub struct ExtendedCap {
pub features: HashSet<Features>,
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<String>,
pub lang: Option<Vec<[char; 2]>>,
pub sex: Option<Sex>,
}
/// 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<u8> 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<T> {
signature: Option<T>,
decryption: Option<T>,
authentication: Option<T>,
}
/// Enum to identify the Key-slots on an OpenPGP card
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum KeyType {

View file

@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// 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<T> From<(Option<T>, Option<T>, Option<T>)> for KeySet<T> {
fn from(tuple: (Option<T>, Option<T>, Option<T>)) -> Self {
Self {
signature: tuple.0,
decryption: tuple.1,
authentication: tuple.2,
}
}
}
impl<T> KeySet<T> {
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<O>(result: nom::IResult<&[u8], O>) -> Result<O, Error> {
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))
}
}

View file

@ -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<Self> {
crate::parse::complete(Tlv::parse(input))
complete(Tlv::parse(input))
}
}
@ -94,7 +95,7 @@ impl TlvEntry {
}
pub fn from(data: &[u8], constructed: bool) -> Result<Self> {
crate::parse::complete(Self::parse(data, constructed))
complete(Self::parse(data, constructed))
}
pub fn serialize(&self) -> Vec<u8> {