Replace hard coded tag values with Tags enum.

Fixes #12
This commit is contained in:
Heiko Schaefer 2022-05-04 11:43:39 +02:00
parent 46d9a2dad9
commit 7854a40b5b
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
7 changed files with 326 additions and 139 deletions

View file

@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! Pre-defined `Command` values for the OpenPGP card application //! Pre-defined `Command` values for the OpenPGP card application
use crate::apdu::command::Command; use crate::apdu::command::Command;
use crate::{Tag, Tags};
/// 7.2.1 SELECT /// 7.2.1 SELECT
/// (select the OpenPGP application on the card) /// (select the OpenPGP application on the card)
@ -17,49 +18,54 @@ pub(crate) fn select_openpgp() -> Command {
) )
} }
/// 7.2.6 GET DATA (tag consists one byte) /// 7.2.6 GET DATA
fn get_data1(tag0: u8) -> Command { fn get_data<T: Into<Tag>>(tag: T) -> Command {
Command::new(0x00, 0xCA, 0, tag0, vec![]) match *tag.into().get() {
} [tag0] => Command::new(0x00, 0xCA, 0, tag0, vec![]),
[tag0, tag1] => Command::new(0x00, 0xCA, tag0, tag1, vec![]),
/// 7.2.6 GET DATA (tag consists of two bytes) _ => panic!("this should never happen"), // FIXME
fn get_data2(tag0: u8, tag1: u8) -> Command { }
Command::new(0x00, 0xCA, tag0, tag1, vec![])
} }
/// GET DO "Application related data" /// GET DO "Application related data"
pub(crate) fn application_related_data() -> Command { pub(crate) fn application_related_data() -> Command {
get_data1(0x6E) get_data(Tags::ApplicationRelatedData)
} }
/// GET DO "private use" /// GET DO "private use"
pub(crate) fn private_use_do(num: u8) -> Command { pub(crate) fn private_use_do(num: u8) -> Command {
get_data2(0x01, num) match num {
1 => get_data(Tags::PrivateUse1),
2 => get_data(Tags::PrivateUse2),
3 => get_data(Tags::PrivateUse3),
4 => get_data(Tags::PrivateUse4),
_ => panic!("this should never happen"), // FIXME
}
} }
/// GET DO "Uniform resource locator" /// GET DO "Uniform resource locator"
pub(crate) fn url() -> Command { pub(crate) fn url() -> Command {
get_data2(0x5F, 0x50) get_data(Tags::Url)
} }
/// GET DO "Cardholder related data" /// GET DO "Cardholder related data"
pub(crate) fn cardholder_related_data() -> Command { pub(crate) fn cardholder_related_data() -> Command {
get_data1(0x65) get_data(Tags::CardholderRelatedData)
} }
/// GET DO "Security support template" /// GET DO "Security support template"
pub(crate) fn security_support_template() -> Command { pub(crate) fn security_support_template() -> Command {
get_data1(0x7A) get_data(Tags::SecuritySupportTemplate)
} }
/// GET DO "Cardholder certificate" /// GET DO "Cardholder certificate"
pub(crate) fn cardholder_certificate() -> Command { pub(crate) fn cardholder_certificate() -> Command {
get_data2(0x7F, 0x21) get_data(Tags::CardholderCertificate)
} }
/// GET DO "Algorithm Information" /// GET DO "Algorithm Information"
pub(crate) fn algo_info() -> Command { pub(crate) fn algo_info() -> Command {
get_data1(0xFA) get_data(Tags::AlgorithmInformation)
} }
/// GET Firmware Version (yubikey specific?) /// GET Firmware Version (yubikey specific?)
@ -98,51 +104,53 @@ pub(crate) fn verify_pw3(pin: Vec<u8>) -> Command {
} }
/// 7.2.8 PUT DATA, /// 7.2.8 PUT DATA,
/// ('tag' must consist of either one or two bytes) pub(crate) fn put_data<T: Into<Tag>>(tag: T, data: Vec<u8>) -> Command {
pub(crate) fn put_data(tag: &[u8], data: Vec<u8>) -> Command { match *tag.into().get() {
assert!(!tag.is_empty() && tag.len() <= 2); [tag0] => Command::new(0x00, 0xda, 0, tag0, data),
[tag0, tag1] => Command::new(0x00, 0xda, tag0, tag1, data),
let (p1, p2) = if tag.len() == 2 { _ => panic!("this should never happen"), // FIXME
(tag[0], tag[1]) }
} else {
(0, tag[0])
};
Command::new(0x00, 0xda, p1, p2, data)
} }
/// PUT DO "private use" /// PUT DO "private use"
pub(crate) fn put_private_use_do(num: u8, data: Vec<u8>) -> Command { pub(crate) fn put_private_use_do(num: u8, data: Vec<u8>) -> Command {
put_data(&[0x01, num], data) match num {
1 => put_data(Tags::PrivateUse1, data),
2 => put_data(Tags::PrivateUse2, data),
3 => put_data(Tags::PrivateUse3, data),
4 => put_data(Tags::PrivateUse4, data),
_ => panic!("this should never happen"), // FIXME
}
} }
/// PUT DO Name /// PUT DO Name
pub(crate) fn put_name(name: Vec<u8>) -> Command { pub(crate) fn put_name(name: Vec<u8>) -> Command {
put_data(&[0x5b], name) put_data(Tags::Name, name)
} }
/// PUT DO Language preferences /// PUT DO Language preferences
pub(crate) fn put_lang(lang: Vec<u8>) -> Command { pub(crate) fn put_lang(lang: Vec<u8>) -> Command {
put_data(&[0x5f, 0x2d], lang) put_data(Tags::LanguagePref, lang)
} }
/// PUT DO Sex /// PUT DO Sex
pub(crate) fn put_sex(sex: u8) -> Command { pub(crate) fn put_sex(sex: u8) -> Command {
put_data(&[0x5f, 0x35], vec![sex]) put_data(Tags::Sex, vec![sex])
} }
/// PUT DO Uniform resource locator (URL) /// PUT DO Uniform resource locator (URL)
pub(crate) fn put_url(url: Vec<u8>) -> Command { pub(crate) fn put_url(url: Vec<u8>) -> Command {
put_data(&[0x5f, 0x50], url) put_data(Tags::Url, url)
} }
/// PUT DO "PW status bytes" /// PUT DO "PW status bytes"
pub(crate) fn put_pw_status(data: Vec<u8>) -> Command { pub(crate) fn put_pw_status(data: Vec<u8>) -> Command {
put_data(&[0xc4], data) put_data(Tags::PWStatusBytes, data)
} }
/// PUT DO "Cardholder certificate" /// PUT DO "Cardholder certificate"
pub(crate) fn put_cardholder_certificate(data: Vec<u8>) -> Command { pub(crate) fn put_cardholder_certificate(data: Vec<u8>) -> Command {
put_data(&[0x7F, 0x21], data) put_data(Tags::CardholderCertificate, data)
} }
/// "RESET RETRY COUNTER" (PW1, user pin) /// "RESET RETRY COUNTER" (PW1, user pin)

View file

@ -8,7 +8,7 @@ use std::convert::{TryFrom, TryInto};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
use crate::{algorithm::Algo, tlv::Tlv, Error, KeySet, KeyType}; use crate::{algorithm::Algo, tlv::Tlv, Error, KeySet, KeyType, Tags};
mod algo_attrs; mod algo_attrs;
mod algo_info; mod algo_info;
@ -34,7 +34,7 @@ pub struct ApplicationRelatedData(pub(crate) Tlv);
impl ApplicationRelatedData { impl ApplicationRelatedData {
/// Get application identifier (AID), ISO 7816-4 /// Get application identifier (AID), ISO 7816-4
pub fn application_id(&self) -> Result<ApplicationIdentifier, Error> { pub fn application_id(&self) -> Result<ApplicationIdentifier, Error> {
let aid = self.0.find(&[0x4f].into()); let aid = self.0.find(Tags::ApplicationIdentifier);
if let Some(aid) = aid { if let Some(aid) = aid {
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?) Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
@ -45,7 +45,7 @@ impl ApplicationRelatedData {
/// Get historical bytes /// Get historical bytes
pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> { pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> {
let hist = self.0.find(&[0x5f, 0x52].into()); let hist = self.0.find(Tags::HistoricalBytes);
if let Some(hist) = hist { if let Some(hist) = hist {
log::trace!("Historical bytes: {:x?}", hist); log::trace!("Historical bytes: {:x?}", hist);
@ -60,7 +60,7 @@ impl ApplicationRelatedData {
/// Get extended length information (ISO 7816-4), which /// Get extended length information (ISO 7816-4), which
/// contains maximum number of bytes for command and response. /// contains maximum number of bytes for command and response.
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> { pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
let eli = self.0.find(&[0x7f, 0x66].into()); let eli = self.0.find(Tags::ExtendedLengthInformation);
log::trace!("Extended length information: {:x?}", eli); log::trace!("Extended length information: {:x?}", eli);
@ -89,7 +89,7 @@ impl ApplicationRelatedData {
let version = app_id.version(); let version = app_id.version();
// get from cached "application related data" // get from cached "application related data"
let ecap = self.0.find(&[0xc0].into()); let ecap = self.0.find(Tags::ExtendedCapabilities);
if let Some(ecap) = ecap { if let Some(ecap) = ecap {
Ok(ExtendedCapabilities::try_from(( Ok(ExtendedCapabilities::try_from((
@ -105,7 +105,7 @@ impl ApplicationRelatedData {
/// Get algorithm attributes (for each key type) /// Get algorithm attributes (for each key type)
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> { pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
let aa = self.0.find(&[key_type.algorithm_tag()].into()); let aa = self.0.find(key_type.algorithm_tag());
if let Some(aa) = aa { if let Some(aa) = aa {
Algo::try_from(&aa.serialize()[..]) Algo::try_from(&aa.serialize()[..])
@ -119,7 +119,7 @@ impl ApplicationRelatedData {
/// Get PW status Bytes /// Get PW status Bytes
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> { pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
let psb = self.0.find(&[0xc4].into()); let psb = self.0.find(Tags::PWStatusBytes);
if let Some(psb) = psb { if let Some(psb) = psb {
let pws = (&psb.serialize()[..]).try_into()?; let pws = (&psb.serialize()[..]).try_into()?;
@ -137,7 +137,7 @@ impl ApplicationRelatedData {
/// Fingerprint, per key type. /// Fingerprint, per key type.
/// Zero bytes indicate a not defined private key. /// Zero bytes indicate a not defined private key.
pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> { pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> {
let fp = self.0.find(&[0xc5].into()); let fp = self.0.find(Tags::Fingerprints);
if let Some(fp) = fp { if let Some(fp) = fp {
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?; let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
@ -151,7 +151,7 @@ impl ApplicationRelatedData {
} }
pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> { pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> {
let fp = self.0.find(&[0xc6].into()); let fp = self.0.find(Tags::CaFingerprints);
if let Some(fp) = fp { if let Some(fp) = fp {
// FIXME: using a KeySet is a weird hack // FIXME: using a KeySet is a weird hack
@ -169,7 +169,7 @@ impl ApplicationRelatedData {
/// Generation dates/times of key pairs /// Generation dates/times of key pairs
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> { pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> {
let kg = self.0.find(&[0xcd].into()); let kg = self.0.find(Tags::GenerationTimes);
if let Some(kg) = kg { if let Some(kg) = kg {
let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?; let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?;
@ -185,7 +185,7 @@ impl ApplicationRelatedData {
} }
pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> { pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> {
let ki = self.0.find(&[0xde].into()); let ki = self.0.find(Tags::KeyInformation);
// TODO: return an error in .into(), if the format of the value is bad // TODO: return an error in .into(), if the format of the value is bad
@ -193,7 +193,7 @@ impl ApplicationRelatedData {
} }
pub fn uif_pso_cds(&self) -> Result<Option<UIF>, Error> { pub fn uif_pso_cds(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd6].into()); let uif = self.0.find(Tags::UifSig);
match uif { match uif {
None => Ok(None), None => Ok(None),
@ -202,7 +202,7 @@ impl ApplicationRelatedData {
} }
pub fn uif_pso_dec(&self) -> Result<Option<UIF>, Error> { pub fn uif_pso_dec(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd7].into()); let uif = self.0.find(Tags::UifDec);
match uif { match uif {
None => Ok(None), None => Ok(None),
@ -211,7 +211,7 @@ impl ApplicationRelatedData {
} }
pub fn uif_pso_aut(&self) -> Result<Option<UIF>, Error> { pub fn uif_pso_aut(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd8].into()); let uif = self.0.find(Tags::UifAuth);
match uif { match uif {
None => Ok(None), None => Ok(None),
@ -220,7 +220,7 @@ impl ApplicationRelatedData {
} }
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> { pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
let uif = self.0.find(&[0xd9].into()); let uif = self.0.find(Tags::UifAttestation);
match uif { match uif {
None => Ok(None), None => Ok(None),

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! Cardholder Related Data (see spec pg. 22) //! Cardholder Related Data (see spec pg. 22)
@ -7,6 +7,7 @@ use std::convert::TryFrom;
use crate::card_do::{CardholderRelatedData, Lang, Sex}; use crate::card_do::{CardholderRelatedData, Lang, Sex};
use crate::tlv::{value::Value, Tlv}; use crate::tlv::{value::Value, Tlv};
use crate::Tags;
impl CardholderRelatedData { impl CardholderRelatedData {
pub fn name(&self) -> Option<&[u8]> { pub fn name(&self) -> Option<&[u8]> {
@ -35,11 +36,11 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
fn try_from(data: &[u8]) -> Result<Self, crate::Error> { fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
let value = Value::from(data, true)?; let value = Value::from(data, true)?;
let tlv = Tlv::new([0x65], value); let tlv = Tlv::new(Tags::CardholderRelatedData, value);
let name: Option<Vec<u8>> = tlv.find(&[0x5b].into()).map(|v| v.serialize().to_vec()); let name: Option<Vec<u8>> = tlv.find(Tags::Name).map(|v| v.serialize().to_vec());
let lang: Option<Vec<Lang>> = tlv.find(&[0x5f, 0x2d].into()).map(|v| { let lang: Option<Vec<Lang>> = tlv.find(Tags::LanguagePref).map(|v| {
v.serialize() v.serialize()
.chunks(2) .chunks(2)
.map(|c| match c.len() { .map(|c| match c.len() {
@ -51,7 +52,7 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
}); });
let sex = tlv let sex = tlv
.find(&[0x5f, 0x35].into()) .find(Tags::Sex)
.map(|v| v.serialize()) .map(|v| v.serialize())
.filter(|v| v.len() == 1) .filter(|v| v.len() == 1)
.map(|v| Sex::from(v[0])); .map(|v| Sex::from(v[0]));

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! Generate and import keys //! Generate and import keys
@ -16,7 +16,7 @@ use crate::crypto_data::{
}; };
use crate::openpgp::OpenPgpTransaction; use crate::openpgp::OpenPgpTransaction;
use crate::tlv::{length::tlv_encode_length, value::Value, Tlv}; use crate::tlv::{length::tlv_encode_length, value::Value, Tlv};
use crate::{apdu, Error, KeyType}; use crate::{apdu, Error, KeyType, Tag, Tags};
/// Generate asymmetric key pair on the card. /// Generate asymmetric key pair on the card.
/// ///
@ -92,10 +92,10 @@ pub(crate) fn gen_key_with_metadata(
/// Transform a public key Tlv from the card into PublicKeyMaterial /// Transform a public key Tlv from the card into PublicKeyMaterial
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> { fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> {
let n = tlv.find(&[0x81].into()); let n = tlv.find(Tag::from([0x81]));
let v = tlv.find(&[0x82].into()); let v = tlv.find(Tag::from([0x82]));
let ec = tlv.find(&[0x86].into()); let ec = tlv.find(Tag::from([0x86]));
match (n, v, ec) { match (n, v, ec) {
(Some(n), Some(v), None) => { (Some(n), Some(v), None) => {
@ -454,14 +454,14 @@ fn rsa_key_import_cmd(
} }
// Assemble the DOs for upload // Assemble the DOs for upload
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data)); let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data)); let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
// "Control Reference Template" // "Control Reference Template"
let crt = control_reference_template(key_type)?; let crt = control_reference_template(key_type)?;
// "Extended header list (DO 4D)" // "Extended header list (DO 4D)"
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk])); let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
// Return the full key import command // Return the full key import command
Ok(commands::key_import(ehl.serialize().to_vec())) Ok(commands::key_import(ehl.serialize().to_vec()))
@ -505,16 +505,16 @@ fn ecc_key_import_cmd(
// Assemble DOs // Assemble DOs
// "Cardholder private key template" // "Cardholder private key template"
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data)); let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
// "Cardholder private key" // "Cardholder private key"
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data)); let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
// "Control Reference Template" // "Control Reference Template"
let crt = control_reference_template(key_type)?; let crt = control_reference_template(key_type)?;
// "Extended header list (DO 4D)" (contains the three inner TLV) // "Extended header list (DO 4D)" (contains the three inner TLV)
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk])); let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
// key import command // key import command
Ok(commands::key_import(ehl.serialize().to_vec())) Ok(commands::key_import(ehl.serialize().to_vec()))
@ -524,10 +524,10 @@ fn ecc_key_import_cmd(
fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> { fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> {
// "Control Reference Template" (0xB8 | 0xB6 | 0xA4) // "Control Reference Template" (0xB8 | 0xB6 | 0xA4)
let tag = match key_type { let tag = match key_type {
KeyType::Decryption => 0xB8, KeyType::Decryption => Tags::CrtKeyConfidentiality,
KeyType::Signing => 0xB6, KeyType::Signing => Tags::CrtKeySignature,
KeyType::Authentication => 0xA4, KeyType::Authentication => Tags::CrtKeyAuthentication,
_ => return Err(Error::InternalError("Unexpected KeyType".to_string())), _ => return Err(Error::InternalError("Unexpected KeyType".to_string())),
}; };
Ok(Tlv::new([tag], Value::S(vec![]))) Ok(Tlv::new(tag, Value::S(vec![])))
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
//! Client library for //! Client library for
@ -25,6 +25,8 @@
//! crate offers a higher level wrapper based on the //! crate offers a higher level wrapper based on the
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation. //! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
extern crate core;
pub mod algorithm; pub mod algorithm;
pub(crate) mod apdu; pub(crate) mod apdu;
pub mod card_do; pub mod card_do;
@ -117,7 +119,10 @@ pub trait CardTransaction {
log::trace!(" ARD value: {:x?}", value); log::trace!(" ARD value: {:x?}", value);
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value))) Ok(ApplicationRelatedData(Tlv::new(
Tags::ApplicationRelatedData,
value,
)))
} }
/// Get a CardApp based on a CardTransaction. /// Get a CardApp based on a CardTransaction.
@ -241,6 +246,165 @@ impl CardCaps {
} }
} }
/// Tags, as specified and used in the OpenPGP card 3.4.1 spec.
/// All tags in OpenPGP card are either 1 or 2 bytes long.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[non_exhaustive]
#[allow(dead_code)]
pub(crate) enum Tags {
// BER identifiers
OctetString,
Null,
ObjectIdentifier,
Sequence,
// GET DATA
PrivateUse1,
PrivateUse2,
PrivateUse3,
PrivateUse4,
ApplicationIdentifier,
LoginData,
Url,
HistoricalBytes,
CardholderRelatedData,
Name,
LanguagePref,
Sex,
ApplicationRelatedData,
ExtendedLengthInformation,
GeneralFeatureManagement,
DiscretionaryDataObjects,
ExtendedCapabilities,
AlgorithmAttributesSignature,
AlgorithmAttributesDecryption,
AlgorithmAttributesAuthentication,
PWStatusBytes,
Fingerprints,
CaFingerprints,
GenerationTimes,
KeyInformation,
UifSig,
UifDec,
UifAuth,
UifAttestation,
SecuritySupportTemplate,
DigitalSignatureCounter,
CardholderCertificate,
AlgorithmAttributesAttestation,
FingerprintAttestation,
CaFingerprintAttestation,
GenerationTimeAttestation,
KdfDo,
AlgorithmInformation,
CertificateSecureMessaging,
AttestationCertificate,
// PUT DATA (additional Tags that don't get used for GET DATA)
FingerprintSignature,
FingerprintDecryption,
FingerprintAuthentication,
CaFingerprint1,
CaFingerprint2,
CaFingerprint3,
GenerationTimeSignature,
GenerationTimeDecryption,
GenerationTimeAuthentication,
// FIXME: +D1, D2
ResettingCode,
// OTHER
// 4.4.3.12 Private Key Template
ExtendedHeaderList,
CardholderPrivateKeyTemplate,
ConcatenatedKeyData,
// 7.2.14 GENERATE ASYMMETRIC KEY PAIR
PublicKey,
// 7.2.11 PSO: DECIPHER
Cipher,
ExternalPublicKey,
// 7.2.5 SELECT DATA
GeneralReference,
TagList,
// 4.4.3.12 Private Key Template
CrtKeySignature,
CrtKeyConfidentiality,
CrtKeyAuthentication,
}
impl From<Tags> for Tag {
fn from(t: Tags) -> Self {
match t {
// BER identifiers https://en.wikipedia.org/wiki/X.690#BER_encoding
Tags::OctetString => [0x04].into(),
Tags::Null => [0x05].into(),
Tags::ObjectIdentifier => [0x06].into(),
Tags::Sequence => [0x30].into(),
// GET DATA
Tags::PrivateUse1 => [0x01, 0x01].into(),
Tags::PrivateUse2 => [0x01, 0x02].into(),
Tags::PrivateUse3 => [0x01, 0x03].into(),
Tags::PrivateUse4 => [0x01, 0x04].into(),
Tags::ApplicationIdentifier => [0x4f].into(),
Tags::LoginData => [0x5e].into(),
Tags::Url => [0x5f, 0x50].into(),
Tags::HistoricalBytes => [0x5f, 0x52].into(),
Tags::CardholderRelatedData => [0x65].into(),
Tags::Name => [0x5b].into(),
Tags::LanguagePref => [0x5f, 0x2d].into(),
Tags::Sex => [0x5f, 0x35].into(),
Tags::ApplicationRelatedData => [0x6e].into(),
Tags::ExtendedLengthInformation => [0x7f, 0x66].into(),
Tags::GeneralFeatureManagement => [0x7f, 0x74].into(),
Tags::DiscretionaryDataObjects => [0x73].into(),
Tags::ExtendedCapabilities => [0xc0].into(),
Tags::AlgorithmAttributesSignature => [0xc1].into(),
Tags::AlgorithmAttributesDecryption => [0xc2].into(),
Tags::AlgorithmAttributesAuthentication => [0xc3].into(),
Tags::PWStatusBytes => [0xc4].into(),
Tags::Fingerprints => [0xc5].into(),
Tags::CaFingerprints => [0xc6].into(),
Tags::GenerationTimes => [0xcd].into(),
Tags::KeyInformation => [0xde].into(),
Tags::UifSig => [0xd6].into(),
Tags::UifDec => [0xd7].into(),
Tags::UifAuth => [0xd8].into(),
Tags::UifAttestation => [0xd9].into(),
Tags::SecuritySupportTemplate => [0x7a].into(),
Tags::DigitalSignatureCounter => [0x93].into(),
Tags::CardholderCertificate => [0x7f, 0x21].into(),
Tags::AlgorithmAttributesAttestation => [0xda].into(),
Tags::FingerprintAttestation => [0xdb].into(),
Tags::CaFingerprintAttestation => [0xdc].into(),
Tags::GenerationTimeAttestation => [0xdd].into(),
Tags::KdfDo => [0xf9].into(),
Tags::AlgorithmInformation => [0xfa].into(),
Tags::CertificateSecureMessaging => [0xfb].into(),
Tags::AttestationCertificate => [0xfc].into(),
// PUT DATA
Tags::FingerprintSignature => [0xc7].into(),
Tags::FingerprintDecryption => [0xc8].into(),
Tags::FingerprintAuthentication => [0xc9].into(),
Tags::CaFingerprint1 => [0xca].into(),
Tags::CaFingerprint2 => [0xcb].into(),
Tags::CaFingerprint3 => [0xcc].into(),
Tags::GenerationTimeSignature => [0xce].into(),
Tags::GenerationTimeDecryption => [0xcf].into(),
Tags::GenerationTimeAuthentication => [0xd0].into(),
Tags::ResettingCode => [0xd3].into(),
// OTHER
Tags::ExtendedHeaderList => [0x4d].into(),
Tags::CardholderPrivateKeyTemplate => [0x7f, 0x48].into(),
Tags::ConcatenatedKeyData => [0x5f, 0x48].into(),
Tags::PublicKey => [0x7f, 0x49].into(),
Tags::Cipher => [0xa6].into(),
Tags::ExternalPublicKey => [0x86].into(),
Tags::GeneralReference => [0x60].into(),
Tags::TagList => [0x5c].into(),
Tags::CrtKeySignature => [0xb6].into(),
Tags::CrtKeyConfidentiality => [0xb8].into(),
Tags::CrtKeyAuthentication => [0xa4].into(),
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum PinType { pub enum PinType {
Sign, Sign,
@ -270,39 +434,42 @@ pub enum KeyType {
impl KeyType { impl KeyType {
/// Get C1/C2/C3/DA values for this KeyTypes, to use as Tag /// Get C1/C2/C3/DA values for this KeyTypes, to use as Tag
fn algorithm_tag(&self) -> u8 { fn algorithm_tag(&self) -> Tag {
match self { match self {
Self::Signing => 0xC1, Self::Signing => Tags::AlgorithmAttributesSignature,
Self::Decryption => 0xC2, Self::Decryption => Tags::AlgorithmAttributesDecryption,
Self::Authentication => 0xC3, Self::Authentication => Tags::AlgorithmAttributesAuthentication,
Self::Attestation => 0xDA, Self::Attestation => Tags::AlgorithmAttributesAttestation,
} }
.into()
} }
/// Get C7/C8/C9/DB values for this KeyTypes, to use as Tag. /// Get C7/C8/C9/DB values for this KeyTypes, to use as Tag.
/// ///
/// (NOTE: these Tags are only used for "PUT DO", but GETting /// (NOTE: these Tags are only used for "PUT DO", but GETting
/// fingerprint information from the card uses the combined Tag C5) /// fingerprint information from the card uses the combined Tag C5)
fn fingerprint_put_tag(&self) -> u8 { fn fingerprint_put_tag(&self) -> Tag {
match self { match self {
Self::Signing => 0xC7, Self::Signing => Tags::FingerprintSignature,
Self::Decryption => 0xC8, Self::Decryption => Tags::FingerprintDecryption,
Self::Authentication => 0xC9, Self::Authentication => Tags::FingerprintAuthentication,
Self::Attestation => 0xDB, Self::Attestation => Tags::FingerprintAttestation,
} }
.into()
} }
/// Get CE/CF/D0/DD values for this KeyTypes, to use as Tag. /// Get CE/CF/D0/DD values for this KeyTypes, to use as Tag.
/// ///
/// (NOTE: these Tags are only used for "PUT DO", but GETting /// (NOTE: these Tags are only used for "PUT DO", but GETting
/// timestamp information from the card uses the combined Tag CD) /// timestamp information from the card uses the combined Tag CD)
fn timestamp_put_tag(&self) -> u8 { fn timestamp_put_tag(&self) -> Tag {
match self { match self {
Self::Signing => 0xCE, Self::Signing => Tags::GenerationTimeSignature,
Self::Decryption => 0xCF, Self::Decryption => Tags::GenerationTimeDecryption,
Self::Authentication => 0xD0, Self::Authentication => Tags::GenerationTimeAuthentication,
Self::Attestation => 0xDD, Self::Attestation => Tags::GenerationTimeAttestation,
} }
.into()
} }
} }

View file

@ -14,6 +14,7 @@ use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial}
use crate::tlv::{value::Value, Tlv}; use crate::tlv::{value::Value, Tlv};
use crate::{ use crate::{
apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes, apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes,
Tag, Tags,
}; };
/// An OpenPGP card access object, backed by a CardBackend implementation. /// An OpenPGP card access object, backed by a CardBackend implementation.
@ -116,7 +117,7 @@ impl<'a> OpenPgpTransaction<'a> {
resp.check_ok()?; resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?; let tlv = Tlv::try_from(resp.data()?)?;
let res = tlv.find(&[0x93].into()).ok_or_else(|| { let res = tlv.find(Tag::from([0x93])).ok_or_else(|| {
Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string()) Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string())
})?; })?;
@ -192,8 +193,8 @@ impl<'a> OpenPgpTransaction<'a> {
log::info!("OpenPgpTransaction: select_data"); log::info!("OpenPgpTransaction: select_data");
let tlv = Tlv::new( let tlv = Tlv::new(
[0x60], Tags::GeneralReference,
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]), Value::C(vec![Tlv::new(Tags::TagList, Value::S(tag.to_vec()))]),
); );
let data = tlv.serialize(); let data = tlv.serialize();
@ -487,13 +488,13 @@ impl<'a> OpenPgpTransaction<'a> {
// -> 86 xx External Public Key // -> 86 xx External Public Key
// External Public Key // External Public Key
let epk = Tlv::new([0x86], Value::S(eph.to_vec())); let epk = Tlv::new(Tags::ExternalPublicKey, Value::S(eph.to_vec()));
// Public Key DO // Public Key DO
let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk])); let pkdo = Tlv::new(Tags::PublicKey, Value::C(vec![epk]));
// Cipher DO // Cipher DO
let cdo = Tlv::new([0xa6], Value::C(vec![pkdo])); let cdo = Tlv::new(Tags::Cipher, Value::C(vec![pkdo]));
self.pso_decipher(cdo.serialize()) self.pso_decipher(cdo.serialize())
} }
@ -652,7 +653,7 @@ impl<'a> OpenPgpTransaction<'a> {
log::info!("OpenPgpTransaction: set_algorithm_attributes"); log::info!("OpenPgpTransaction: set_algorithm_attributes");
// Command to PUT the algorithm attributes // Command to PUT the algorithm attributes
let cmd = commands::put_data(&[key_type.algorithm_tag()], algo.to_data_object()?); let cmd = commands::put_data(key_type.algorithm_tag(), algo.to_data_object()?);
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
@ -684,7 +685,7 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_fingerprint(&mut self, fp: Fingerprint, key_type: KeyType) -> Result<(), Error> { pub fn set_fingerprint(&mut self, fp: Fingerprint, key_type: KeyType) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_fingerprint"); log::info!("OpenPgpTransaction: set_fingerprint");
let fp_cmd = commands::put_data(&[key_type.fingerprint_put_tag()], fp.as_bytes().to_vec()); let fp_cmd = commands::put_data(key_type.fingerprint_put_tag(), fp.as_bytes().to_vec());
apdu::send_command(self.tx(), fp_cmd, false)?.try_into() apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
} }
@ -692,21 +693,21 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_ca_fingerprint_1(&mut self, fp: Fingerprint) -> Result<(), Error> { pub fn set_ca_fingerprint_1(&mut self, fp: Fingerprint) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_ca_fingerprint_1"); log::info!("OpenPgpTransaction: set_ca_fingerprint_1");
let fp_cmd = commands::put_data(&[0xCA], fp.as_bytes().to_vec()); let fp_cmd = commands::put_data(Tags::CaFingerprint1, fp.as_bytes().to_vec());
apdu::send_command(self.tx(), fp_cmd, false)?.try_into() apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
} }
pub fn set_ca_fingerprint_2(&mut self, fp: Fingerprint) -> Result<(), Error> { pub fn set_ca_fingerprint_2(&mut self, fp: Fingerprint) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_ca_fingerprint_2"); log::info!("OpenPgpTransaction: set_ca_fingerprint_2");
let fp_cmd = commands::put_data(&[0xCB], fp.as_bytes().to_vec()); let fp_cmd = commands::put_data(Tags::CaFingerprint2, fp.as_bytes().to_vec());
apdu::send_command(self.tx(), fp_cmd, false)?.try_into() apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
} }
pub fn set_ca_fingerprint_3(&mut self, fp: Fingerprint) -> Result<(), Error> { pub fn set_ca_fingerprint_3(&mut self, fp: Fingerprint) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_ca_fingerprint_3"); log::info!("OpenPgpTransaction: set_ca_fingerprint_3");
let fp_cmd = commands::put_data(&[0xCC], fp.as_bytes().to_vec()); let fp_cmd = commands::put_data(Tags::CaFingerprint3, fp.as_bytes().to_vec());
apdu::send_command(self.tx(), fp_cmd, false)?.try_into() apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
} }
@ -726,7 +727,7 @@ impl<'a> OpenPgpTransaction<'a> {
.copied() .copied()
.collect(); .collect();
let time_cmd = commands::put_data(&[key_type.timestamp_put_tag()], time_value); let time_cmd = commands::put_data(key_type.timestamp_put_tag(), time_value);
apdu::send_command(self.tx(), time_cmd, false)?.try_into() apdu::send_command(self.tx(), time_cmd, false)?.try_into()
} }
@ -740,7 +741,7 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_resetting_code(&mut self, resetting_code: &[u8]) -> Result<(), Error> { pub fn set_resetting_code(&mut self, resetting_code: &[u8]) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_resetting_code"); log::info!("OpenPgpTransaction: set_resetting_code");
let cmd = commands::put_data(&[0xd3], resetting_code.to_vec()); let cmd = commands::put_data(Tags::ResettingCode, resetting_code.to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
@ -750,7 +751,7 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> { pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_cds"); log::info!("OpenPgpTransaction: set_uif_pso_cds");
let cmd = commands::put_data(&[0xd6], uif.as_bytes().to_vec()); let cmd = commands::put_data(Tags::UifSig, uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
@ -758,7 +759,7 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> { pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_dec"); log::info!("OpenPgpTransaction: set_uif_pso_dec");
let cmd = commands::put_data(&[0xd7], uif.as_bytes().to_vec()); let cmd = commands::put_data(Tags::UifDec, uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
@ -766,7 +767,7 @@ impl<'a> OpenPgpTransaction<'a> {
pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> { pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> {
log::info!("OpenPgpTransaction: set_uif_pso_aut"); log::info!("OpenPgpTransaction: set_uif_pso_aut");
let cmd = commands::put_data(&[0xd8], uif.as_bytes().to_vec()); let cmd = commands::put_data(Tags::UifAuth, uif.as_bytes().to_vec());
apdu::send_command(self.tx(), cmd, false)?.try_into() apdu::send_command(self.tx(), cmd, false)?.try_into()
} }
@ -870,20 +871,20 @@ fn digestinfo(hash: Hash) -> Vec<u8> {
match hash { match hash {
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => { Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
let tlv = Tlv::new( let tlv = Tlv::new(
[0x30], Tags::Sequence,
Value::C(vec![ Value::C(vec![
Tlv::new( Tlv::new(
[0x30], Tags::Sequence,
Value::C(vec![ Value::C(vec![
Tlv::new( Tlv::new(
[0x06], Tags::ObjectIdentifier,
// unwrapping is ok, for SHA* // unwrapping is ok, for SHA*
Value::S(hash.oid().unwrap().to_vec()), Value::S(hash.oid().unwrap().to_vec()),
), ),
Tlv::new([0x05], Value::S(vec![])), Tlv::new(Tags::Null, Value::S(vec![])),
]), ]),
), ),
Tlv::new([0x04], Value::S(hash.digest().to_vec())), Tlv::new(Tags::OctetString, Value::S(hash.digest().to_vec())),
]), ]),
); );

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
pub(crate) mod length; pub(crate) mod length;
@ -28,13 +28,14 @@ impl Tlv {
} }
/// Find the first occurrence of `tag` and return its value (if any) /// Find the first occurrence of `tag` and return its value (if any)
pub fn find(&self, tag: &Tag) -> Option<&Value> { pub fn find<T: Clone + Into<Tag>>(&self, tag: T) -> Option<&Value> {
if &self.tag == tag { let t: Tag = tag.clone().into();
if self.tag == t {
Some(&self.value) Some(&self.value)
} else { } else {
if let Value::C(inner) = &self.value { if let Value::C(inner) = &self.value {
for tlv in inner { for tlv in inner {
if let Some(found) = tlv.find(tag) { if let Some(found) = tlv.find(tag.clone()) {
return Some(found); return Some(found);
} }
} }
@ -84,12 +85,12 @@ mod test {
use std::convert::TryFrom; use std::convert::TryFrom;
use super::{Tlv, Value}; use super::{Tlv, Value};
use crate::Error; use crate::{Error, Tags};
#[test] #[test]
fn test_tlv0() { fn test_tlv0() {
let cpkt = Tlv::new( let cpkt = Tlv::new(
[0x7F, 0x48], Tags::CardholderPrivateKeyTemplate,
Value::S(vec![ Value::S(vec![
0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00, 0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00,
]), ]),
@ -109,16 +110,22 @@ mod test {
assert_eq!( assert_eq!(
tlv, tlv,
Tlv::new([0x5b], Value::S(hex!("546573743C3C5465737469").to_vec())) Tlv::new(
Tags::Name,
Value::S(hex!("546573743C3C5465737469").to_vec())
)
); );
let (input, tlv) = Tlv::parse(input).unwrap(); let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv, Tlv::new([0x5f, 0x2d], Value::S(hex!("6465").to_vec()))); assert_eq!(
tlv,
Tlv::new(Tags::LanguagePref, Value::S(hex!("6465").to_vec()))
);
let (input, tlv) = Tlv::parse(input).unwrap(); let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv, Tlv::new([0x5f, 0x35], Value::S(hex!("31").to_vec()))); assert_eq!(tlv, Tlv::new(Tags::Sex, Value::S(hex!("31").to_vec())));
assert!(input.is_empty()); assert!(input.is_empty());
@ -136,69 +143,69 @@ mod test {
assert_eq!(serialized, data.to_vec()); assert_eq!(serialized, data.to_vec());
// outermost layer contains all bytes as value // outermost layer contains all bytes as value
let value = tlv.find(&[0x6e].into()).unwrap(); let value = tlv.find(Tags::ApplicationRelatedData).unwrap();
assert_eq!(value.serialize(), assert_eq!(value.serialize(),
hex!("4f10d27600012401030400061601918000005f520800730000e00590007f740381012073820110c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020")); hex!("4f10d27600012401030400061601918000005f520800730000e00590007f740381012073820110c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
// get and verify data for ecap tag // get and verify data for ecap tag
let value = tlv.find(&[0xc0].into()).unwrap(); let value = tlv.find(Tags::ExtendedCapabilities).unwrap();
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000")); assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
let value = tlv.find(&[0x4f].into()).unwrap(); let value = tlv.find(Tags::ApplicationIdentifier).unwrap();
assert_eq!(value.serialize(), hex!("d2760001240103040006160191800000")); assert_eq!(value.serialize(), hex!("d2760001240103040006160191800000"));
let value = tlv.find(&[0x5f, 0x52].into()).unwrap(); let value = tlv.find(Tags::HistoricalBytes).unwrap();
assert_eq!(value.serialize(), hex!("00730000e0059000")); assert_eq!(value.serialize(), hex!("00730000e0059000"));
let value = tlv.find(&[0x7f, 0x74].into()).unwrap(); let value = tlv.find(Tags::GeneralFeatureManagement).unwrap();
assert_eq!(value.serialize(), hex!("810120")); assert_eq!(value.serialize(), hex!("810120"));
let value = tlv.find(&[0x73].into()).unwrap(); let value = tlv.find(Tags::DiscretionaryDataObjects).unwrap();
assert_eq!(value.serialize(), hex!("c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020")); assert_eq!(value.serialize(), hex!("c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
let value = tlv.find(&[0xc0].into()).unwrap(); let value = tlv.find(Tags::ExtendedCapabilities).unwrap();
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000")); assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
let value = tlv.find(&[0xc1].into()).unwrap(); let value = tlv.find(Tags::AlgorithmAttributesSignature).unwrap();
assert_eq!(value.serialize(), hex!("010800001100")); assert_eq!(value.serialize(), hex!("010800001100"));
let value = tlv.find(&[0xc2].into()).unwrap(); let value = tlv.find(Tags::AlgorithmAttributesDecryption).unwrap();
assert_eq!(value.serialize(), hex!("010800001100")); assert_eq!(value.serialize(), hex!("010800001100"));
let value = tlv.find(&[0xc3].into()).unwrap(); let value = tlv.find(Tags::AlgorithmAttributesAuthentication).unwrap();
assert_eq!(value.serialize(), hex!("010800001100")); assert_eq!(value.serialize(), hex!("010800001100"));
let value = tlv.find(&[0xda].into()).unwrap(); let value = tlv.find(Tags::AlgorithmAttributesAttestation).unwrap();
assert_eq!(value.serialize(), hex!("010800001100")); assert_eq!(value.serialize(), hex!("010800001100"));
let value = tlv.find(&[0xc4].into()).unwrap(); let value = tlv.find(Tags::PWStatusBytes).unwrap();
assert_eq!(value.serialize(), hex!("ff7f7f7f030003")); assert_eq!(value.serialize(), hex!("ff7f7f7f030003"));
let value = tlv.find(&[0xc5].into()).unwrap(); let value = tlv.find(Tags::Fingerprints).unwrap();
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
let value = tlv.find(&[0xc6].into()).unwrap(); let value = tlv.find(Tags::CaFingerprints).unwrap();
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
let value = tlv.find(&[0xcd].into()).unwrap(); let value = tlv.find(Tags::GenerationTimes).unwrap();
assert_eq!(value.serialize(), hex!("00000000000000000000000000000000")); assert_eq!(value.serialize(), hex!("00000000000000000000000000000000"));
let value = tlv.find(&[0xde].into()).unwrap(); let value = tlv.find(Tags::KeyInformation).unwrap();
assert_eq!(value.serialize(), hex!("0100020003008102")); assert_eq!(value.serialize(), hex!("0100020003008102"));
let value = tlv.find(&[0x7f, 0x66].into()).unwrap(); let value = tlv.find(Tags::ExtendedLengthInformation).unwrap();
assert_eq!(value.serialize(), hex!("02020bfe02020bfe")); assert_eq!(value.serialize(), hex!("02020bfe02020bfe"));
let value = tlv.find(&[0xd6].into()).unwrap(); let value = tlv.find(Tags::UifSig).unwrap();
assert_eq!(value.serialize(), hex!("0020")); assert_eq!(value.serialize(), hex!("0020"));
let value = tlv.find(&[0xd7].into()).unwrap(); let value = tlv.find(Tags::UifDec).unwrap();
assert_eq!(value.serialize(), hex!("0020")); assert_eq!(value.serialize(), hex!("0020"));
let value = tlv.find(&[0xd8].into()).unwrap(); let value = tlv.find(Tags::UifAuth).unwrap();
assert_eq!(value.serialize(), hex!("0020")); assert_eq!(value.serialize(), hex!("0020"));
let value = tlv.find(&[0xd9].into()).unwrap(); let value = tlv.find(Tags::UifAttestation).unwrap();
assert_eq!(value.serialize(), hex!("0020")); assert_eq!(value.serialize(), hex!("0020"));
Ok(()) Ok(())
@ -210,11 +217,14 @@ mod test {
// but has been abridged and changed. It does not represent a // but has been abridged and changed. It does not represent a
// complete valid OpenPGP card DO! // complete valid OpenPGP card DO!
let a = Tlv::new([0x7F, 0x48], Value::S(vec![0x92, 0x03])); let a = Tlv::new(
Tags::CardholderPrivateKeyTemplate,
Value::S(vec![0x92, 0x03]),
);
let b = Tlv::new([0x5F, 0x48], Value::S(vec![0x1, 0x2, 0x3])); let b = Tlv::new(Tags::ConcatenatedKeyData, Value::S(vec![0x1, 0x2, 0x3]));
let tlv = Tlv::new([0x4d], Value::C(vec![a, b])); let tlv = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![a, b]));
assert_eq!( assert_eq!(
tlv.serialize(), tlv.serialize(),