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 sequoia_openpgp::Cert;
|
||||||
|
|
||||||
use openpgp_card::algorithm::AlgoSimple;
|
use openpgp_card::algorithm::AlgoSimple;
|
||||||
|
use openpgp_card::card_data::Sex;
|
||||||
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
|
use openpgp_card::errors::{OcErrorStatus, OpenpgpCardError};
|
||||||
use openpgp_card::{CardApp, KeyType, Sex};
|
use openpgp_card::{CardApp, KeyType};
|
||||||
use openpgp_card_sequoia::{
|
use openpgp_card_sequoia::{
|
||||||
make_cert, public_key_material_to_key, public_to_fingerprint,
|
make_cert, public_key_material_to_key, public_to_fingerprint,
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,12 +32,15 @@ use openpgp::{Cert, Packet};
|
||||||
use sequoia_openpgp as openpgp;
|
use sequoia_openpgp as openpgp;
|
||||||
|
|
||||||
use openpgp_card::algorithm::{Algo, AlgoInfo, Curve};
|
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::{
|
use openpgp_card::{
|
||||||
errors::OpenpgpCardError, ApplicationId, ApplicationRelatedData, CardApp,
|
errors::OpenpgpCardError, CardApp, CardClientBox, CardUploadableKey,
|
||||||
CardClientBox, CardUploadableKey, Cardholder, DecryptMe, EccKey, EccType,
|
DecryptMe, EccKey, EccType, Hash, KeyType, PrivateKeyMaterial,
|
||||||
ExtendedCap, ExtendedLengthInfo, Features, Fingerprint, Hash, Historical,
|
PublicKeyMaterial, RSAKey, Response,
|
||||||
KeySet, KeyType, PWStatus, PrivateKeyMaterial, PublicKeyMaterial, RSAKey,
|
|
||||||
Response, Sex,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::signer::CardSigner;
|
use crate::signer::CardSigner;
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::error::Error;
|
||||||
use sequoia_openpgp::parse::Parse;
|
use sequoia_openpgp::parse::Parse;
|
||||||
use sequoia_openpgp::Cert;
|
use sequoia_openpgp::Cert;
|
||||||
|
|
||||||
|
use openpgp_card::card_data::Sex;
|
||||||
use openpgp_card::{CardApp, KeyType};
|
use openpgp_card::{CardApp, KeyType};
|
||||||
use openpgp_card_pcsc::PcscClient;
|
use openpgp_card_pcsc::PcscClient;
|
||||||
// use openpgp_card_scdc::ScdClient;
|
// use openpgp_card_scdc::ScdClient;
|
||||||
|
@ -108,8 +109,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let res = oc_admin.set_name("Bar<<Foo")?;
|
let res = oc_admin.set_name("Bar<<Foo")?;
|
||||||
println!("set name {:x?}", res);
|
println!("set name {:x?}", res);
|
||||||
|
|
||||||
let res =
|
let res = oc_admin.set_sex(Sex::NotApplicable)?;
|
||||||
oc_admin.set_sex(openpgp_card::Sex::NotApplicable)?;
|
|
||||||
println!("set sex {:x?}", res);
|
println!("set sex {:x?}", res);
|
||||||
|
|
||||||
let res = oc_admin.set_lang("en")?;
|
let res = oc_admin.set_lang("en")?;
|
||||||
|
|
|
@ -10,161 +10,14 @@ use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple, RsaAttrs};
|
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple, RsaAttrs};
|
||||||
use crate::apdu::{commands, response::Response};
|
use crate::apdu::{commands, response::Response};
|
||||||
|
use crate::card_data::{ApplicationRelatedData, Cardholder, Sex};
|
||||||
use crate::errors::OpenpgpCardError;
|
use crate::errors::OpenpgpCardError;
|
||||||
use crate::parse::{fingerprint, key_generation_times};
|
|
||||||
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
|
||||||
use crate::{
|
use crate::{
|
||||||
apdu, keys, ApplicationId, CardCaps, CardClientBox, CardUploadableKey,
|
apdu, keys, CardCaps, CardClientBox, CardUploadableKey, DecryptMe,
|
||||||
Cardholder, DecryptMe, EccType, ExtendedCap, ExtendedLengthInfo,
|
EccType, Hash, KeyType, PublicKeyMaterial,
|
||||||
Fingerprint, Hash, Historical, KeyGeneration, KeySet, KeyType, PWStatus,
|
|
||||||
PublicKeyMaterial, Sex,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 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.
|
/// Direct, low-level, access to OpenPGP card functionality.
|
||||||
///
|
///
|
||||||
/// No checks are performed here (e.g. for valid data lengths).
|
/// 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 nom::{branch, bytes::complete as bytes, number::complete as number};
|
||||||
|
|
||||||
use crate::algorithm::{Algo, Curve, EccAttrs, RsaAttrs};
|
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> {
|
fn parse_oid_cv25519(input: &[u8]) -> nom::IResult<&[u8], Curve> {
|
||||||
map(tag(Curve::Cv25519.oid()), |_| Curve::Cv25519)(input)
|
map(tag(Curve::Cv25519.oid()), |_| Curve::Cv25519)(input)
|
||||||
|
@ -151,6 +152,6 @@ impl TryFrom<&[u8]> for Algo {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self> {
|
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 std::fmt;
|
||||||
|
|
||||||
use crate::algorithm::AlgoInfo;
|
use crate::algorithm::AlgoInfo;
|
||||||
use crate::parse::algo_attrs;
|
use crate::card_data::{algo_attrs, complete};
|
||||||
use crate::{Algo, KeyType};
|
use crate::{Algo, KeyType};
|
||||||
|
|
||||||
impl AlgoInfo {
|
impl AlgoInfo {
|
||||||
|
@ -91,7 +91,7 @@ impl TryFrom<&[u8]> for AlgoInfo {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self> {
|
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 {
|
mod test {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::algorithm::{Algo::*, Curve::*, EccAttrs, RsaAttrs};
|
use crate::algorithm::{Algo::*, AlgoInfo, Curve::*, EccAttrs, RsaAttrs};
|
||||||
use crate::parse::algo_info::AlgoInfo;
|
|
||||||
use crate::{EccType::*, KeyType::*};
|
use crate::{EccType::*, KeyType::*};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
|
@ -5,7 +5,7 @@ use anyhow::Result;
|
||||||
use nom::{bytes::complete as bytes, number::complete as number};
|
use nom::{bytes::complete as bytes, number::complete as number};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::{parse, ApplicationId};
|
use crate::card_data::{complete, ApplicationId};
|
||||||
|
|
||||||
fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationId> {
|
fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationId> {
|
||||||
let (input, _) = bytes::tag([0xd2, 0x76, 0x0, 0x1, 0x24])(input)?;
|
let (input, _) = bytes::tag([0xd2, 0x76, 0x0, 0x1, 0x24])(input)?;
|
||||||
|
@ -33,7 +33,7 @@ impl TryFrom<&[u8]> for ApplicationId {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self> {
|
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 anyhow::Result;
|
||||||
|
|
||||||
|
use crate::card_data::{Cardholder, Sex};
|
||||||
use crate::tlv::tag::Tag;
|
use crate::tlv::tag::Tag;
|
||||||
use crate::tlv::{Tlv, TlvEntry};
|
use crate::tlv::{Tlv, TlvEntry};
|
||||||
use crate::{Cardholder, Sex};
|
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Cardholder {
|
impl TryFrom<&[u8]> for Cardholder {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
|
@ -1,14 +1,14 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use crate::errors::OpenpgpCardError;
|
|
||||||
use crate::parse;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nom::{combinator, number::complete as number, sequence};
|
use nom::{combinator, number::complete as number, sequence};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::convert::TryFrom;
|
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>> {
|
fn features(input: &[u8]) -> nom::IResult<&[u8], HashSet<Features>> {
|
||||||
combinator::map(number::u8, |b| {
|
combinator::map(number::u8, |b| {
|
||||||
|
@ -61,7 +61,7 @@ impl TryFrom<&[u8]> for ExtendedCap {
|
||||||
type Error = OpenpgpCardError;
|
type Error = OpenpgpCardError;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||||
let ec = parse::complete(parse(input))?;
|
let ec = complete(parse(input))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
features: ec.0,
|
features: ec.0,
|
||||||
|
@ -77,7 +77,7 @@ impl TryFrom<&[u8]> for ExtendedCap {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::parse::extended_cap::{ExtendedCap, Features};
|
use crate::card_data::extended_cap::{ExtendedCap, Features};
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
|
@ -4,7 +4,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nom::{bytes::complete::tag, number::complete as number, sequence};
|
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)> {
|
fn parse(input: &[u8]) -> nom::IResult<&[u8], (u16, u16)> {
|
||||||
let (input, (_, cmd, _, resp)) =
|
let (input, (_, cmd, _, resp)) =
|
||||||
|
@ -20,7 +20,7 @@ fn parse(input: &[u8]) -> nom::IResult<&[u8], (u16, u16)> {
|
||||||
|
|
||||||
impl ExtendedLengthInfo {
|
impl ExtendedLengthInfo {
|
||||||
pub fn from(input: &[u8]) -> Result<Self> {
|
pub fn from(input: &[u8]) -> Result<Self> {
|
||||||
let eli = parse::complete(parse(input))?;
|
let eli = complete(parse(input))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
max_command_bytes: eli.0,
|
max_command_bytes: eli.0,
|
|
@ -5,9 +5,8 @@ use anyhow::anyhow;
|
||||||
use nom::{bytes::complete as bytes, combinator, sequence};
|
use nom::{bytes::complete as bytes, combinator, sequence};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::card_data::{Fingerprint, KeySet};
|
||||||
use crate::errors::OpenpgpCardError;
|
use crate::errors::OpenpgpCardError;
|
||||||
use crate::parse::KeySet;
|
|
||||||
use crate::Fingerprint;
|
|
||||||
|
|
||||||
impl From<[u8; 20]> for Fingerprint {
|
impl From<[u8; 20]> for Fingerprint {
|
||||||
fn from(data: [u8; 20]) -> Self {
|
fn from(data: [u8; 20]) -> Self {
|
|
@ -1,8 +1,8 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use crate::card_data::{CardCapabilities, CardServiceData, Historical};
|
||||||
use crate::errors::OpenpgpCardError;
|
use crate::errors::OpenpgpCardError;
|
||||||
use crate::{CardCapabilities, CardServiceData, Historical};
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
impl CardCapabilities {
|
impl CardCapabilities {
|
|
@ -5,9 +5,9 @@ use anyhow::anyhow;
|
||||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
use nom::{combinator, number::complete as number, sequence};
|
use nom::{combinator, number::complete as number, sequence};
|
||||||
|
|
||||||
|
use crate::card_data::KeyGeneration;
|
||||||
|
use crate::card_data::KeySet;
|
||||||
use crate::errors::OpenpgpCardError;
|
use crate::errors::OpenpgpCardError;
|
||||||
use crate::parse::KeySet;
|
|
||||||
use crate::KeyGeneration;
|
|
||||||
|
|
||||||
impl From<KeyGeneration> for DateTime<Utc> {
|
impl From<KeyGeneration> for DateTime<Utc> {
|
||||||
fn from(kg: KeyGeneration) -> Self {
|
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-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use crate::errors::OpenpgpCardError;
|
|
||||||
use crate::PWStatus;
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
|
use crate::card_data::PWStatus;
|
||||||
|
use crate::errors::OpenpgpCardError;
|
||||||
|
|
||||||
impl PWStatus {
|
impl PWStatus {
|
||||||
pub fn try_from(input: &[u8]) -> Result<Self, OpenpgpCardError> {
|
pub fn try_from(input: &[u8]) -> Result<Self, OpenpgpCardError> {
|
||||||
if input.len() == 7 {
|
if input.len() == 7 {
|
|
@ -24,20 +24,18 @@
|
||||||
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
|
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use crate::algorithm::Algo;
|
use crate::algorithm::Algo;
|
||||||
|
|
||||||
pub mod algorithm;
|
pub mod algorithm;
|
||||||
mod apdu;
|
mod apdu;
|
||||||
mod card_app;
|
mod card_app;
|
||||||
|
pub mod card_data;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod keys;
|
mod keys;
|
||||||
mod parse;
|
|
||||||
mod tlv;
|
mod tlv;
|
||||||
|
|
||||||
pub use crate::apdu::response::Response;
|
pub use crate::apdu::response::Response;
|
||||||
pub use crate::card_app::ApplicationRelatedData;
|
|
||||||
pub use crate::card_app::CardApp;
|
pub use crate::card_app::CardApp;
|
||||||
|
|
||||||
/// The CardClient trait defines communication with an OpenPGP card via a
|
/// 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.
|
/// Container for a hash value.
|
||||||
/// These hash values can be signed by the card.
|
/// These hash values can be signed by the card.
|
||||||
pub enum Hash<'a> {
|
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
|
/// Enum to identify the Key-slots on an OpenPGP card
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum KeyType {
|
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 length;
|
||||||
pub mod tag;
|
pub mod tag;
|
||||||
|
|
||||||
|
use crate::card_data::complete;
|
||||||
use tag::Tag;
|
use tag::Tag;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -54,7 +55,7 @@ impl Tlv {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_from(input: &[u8]) -> Result<Self> {
|
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> {
|
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> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
|
|
Loading…
Reference in a new issue