Refactor: move OpenPGP card data structures (and parsing) into the module card_data
This commit is contained in:
parent
0e0602f3d5
commit
44e915d3e0
18 changed files with 405 additions and 399 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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")?;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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]
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
|
@ -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;
|
|
@ -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,
|
|
@ -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 {
|
|
@ -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 {
|
|
@ -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 {
|
363
openpgp-card/src/card_data/mod.rs
Normal file
363
openpgp-card/src/card_data/mod.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
|
Loading…
Reference in a new issue