parent
46d9a2dad9
commit
7854a40b5b
7 changed files with 326 additions and 139 deletions
|
@ -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
|
||||
|
||||
//! Pre-defined `Command` values for the OpenPGP card application
|
||||
|
||||
use crate::apdu::command::Command;
|
||||
use crate::{Tag, Tags};
|
||||
|
||||
/// 7.2.1 SELECT
|
||||
/// (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)
|
||||
fn get_data1(tag0: u8) -> Command {
|
||||
Command::new(0x00, 0xCA, 0, tag0, vec![])
|
||||
/// 7.2.6 GET DATA
|
||||
fn get_data<T: Into<Tag>>(tag: T) -> Command {
|
||||
match *tag.into().get() {
|
||||
[tag0] => Command::new(0x00, 0xCA, 0, tag0, vec![]),
|
||||
[tag0, tag1] => Command::new(0x00, 0xCA, tag0, tag1, vec![]),
|
||||
_ => panic!("this should never happen"), // FIXME
|
||||
}
|
||||
|
||||
/// 7.2.6 GET DATA (tag consists of two bytes)
|
||||
fn get_data2(tag0: u8, tag1: u8) -> Command {
|
||||
Command::new(0x00, 0xCA, tag0, tag1, vec![])
|
||||
}
|
||||
|
||||
/// GET DO "Application related data"
|
||||
pub(crate) fn application_related_data() -> Command {
|
||||
get_data1(0x6E)
|
||||
get_data(Tags::ApplicationRelatedData)
|
||||
}
|
||||
|
||||
/// GET DO "private use"
|
||||
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"
|
||||
pub(crate) fn url() -> Command {
|
||||
get_data2(0x5F, 0x50)
|
||||
get_data(Tags::Url)
|
||||
}
|
||||
|
||||
/// GET DO "Cardholder related data"
|
||||
pub(crate) fn cardholder_related_data() -> Command {
|
||||
get_data1(0x65)
|
||||
get_data(Tags::CardholderRelatedData)
|
||||
}
|
||||
|
||||
/// GET DO "Security support template"
|
||||
pub(crate) fn security_support_template() -> Command {
|
||||
get_data1(0x7A)
|
||||
get_data(Tags::SecuritySupportTemplate)
|
||||
}
|
||||
|
||||
/// GET DO "Cardholder certificate"
|
||||
pub(crate) fn cardholder_certificate() -> Command {
|
||||
get_data2(0x7F, 0x21)
|
||||
get_data(Tags::CardholderCertificate)
|
||||
}
|
||||
|
||||
/// GET DO "Algorithm Information"
|
||||
pub(crate) fn algo_info() -> Command {
|
||||
get_data1(0xFA)
|
||||
get_data(Tags::AlgorithmInformation)
|
||||
}
|
||||
|
||||
/// GET Firmware Version (yubikey specific?)
|
||||
|
@ -98,51 +104,53 @@ pub(crate) fn verify_pw3(pin: Vec<u8>) -> Command {
|
|||
}
|
||||
|
||||
/// 7.2.8 PUT DATA,
|
||||
/// ('tag' must consist of either one or two bytes)
|
||||
pub(crate) fn put_data(tag: &[u8], data: Vec<u8>) -> Command {
|
||||
assert!(!tag.is_empty() && tag.len() <= 2);
|
||||
|
||||
let (p1, p2) = if tag.len() == 2 {
|
||||
(tag[0], tag[1])
|
||||
} else {
|
||||
(0, tag[0])
|
||||
};
|
||||
Command::new(0x00, 0xda, p1, p2, data)
|
||||
pub(crate) fn put_data<T: Into<Tag>>(tag: T, data: Vec<u8>) -> Command {
|
||||
match *tag.into().get() {
|
||||
[tag0] => Command::new(0x00, 0xda, 0, tag0, data),
|
||||
[tag0, tag1] => Command::new(0x00, 0xda, tag0, tag1, data),
|
||||
_ => panic!("this should never happen"), // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
/// PUT DO "private use"
|
||||
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
|
||||
pub(crate) fn put_name(name: Vec<u8>) -> Command {
|
||||
put_data(&[0x5b], name)
|
||||
put_data(Tags::Name, name)
|
||||
}
|
||||
|
||||
/// PUT DO Language preferences
|
||||
pub(crate) fn put_lang(lang: Vec<u8>) -> Command {
|
||||
put_data(&[0x5f, 0x2d], lang)
|
||||
put_data(Tags::LanguagePref, lang)
|
||||
}
|
||||
|
||||
/// PUT DO Sex
|
||||
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)
|
||||
pub(crate) fn put_url(url: Vec<u8>) -> Command {
|
||||
put_data(&[0x5f, 0x50], url)
|
||||
put_data(Tags::Url, url)
|
||||
}
|
||||
|
||||
/// PUT DO "PW status bytes"
|
||||
pub(crate) fn put_pw_status(data: Vec<u8>) -> Command {
|
||||
put_data(&[0xc4], data)
|
||||
put_data(Tags::PWStatusBytes, data)
|
||||
}
|
||||
|
||||
/// PUT DO "Cardholder certificate"
|
||||
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)
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::convert::{TryFrom, TryInto};
|
|||
use std::fmt::{Display, Formatter};
|
||||
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_info;
|
||||
|
@ -34,7 +34,7 @@ pub struct ApplicationRelatedData(pub(crate) Tlv);
|
|||
impl ApplicationRelatedData {
|
||||
/// Get application identifier (AID), ISO 7816-4
|
||||
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 {
|
||||
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
||||
|
@ -45,7 +45,7 @@ impl ApplicationRelatedData {
|
|||
|
||||
/// Get historical bytes
|
||||
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 {
|
||||
log::trace!("Historical bytes: {:x?}", hist);
|
||||
|
@ -60,7 +60,7 @@ impl ApplicationRelatedData {
|
|||
/// Get extended length information (ISO 7816-4), which
|
||||
/// contains maximum number of bytes for command and response.
|
||||
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);
|
||||
|
||||
|
@ -89,7 +89,7 @@ impl ApplicationRelatedData {
|
|||
let version = app_id.version();
|
||||
|
||||
// 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 {
|
||||
Ok(ExtendedCapabilities::try_from((
|
||||
|
@ -105,7 +105,7 @@ impl ApplicationRelatedData {
|
|||
|
||||
/// Get algorithm attributes (for each key type)
|
||||
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 {
|
||||
Algo::try_from(&aa.serialize()[..])
|
||||
|
@ -119,7 +119,7 @@ impl ApplicationRelatedData {
|
|||
|
||||
/// Get PW status Bytes
|
||||
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 {
|
||||
let pws = (&psb.serialize()[..]).try_into()?;
|
||||
|
@ -137,7 +137,7 @@ impl ApplicationRelatedData {
|
|||
/// Fingerprint, per key type.
|
||||
/// Zero bytes indicate a not defined private key.
|
||||
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 {
|
||||
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
|
||||
|
@ -151,7 +151,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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 {
|
||||
// FIXME: using a KeySet is a weird hack
|
||||
|
@ -169,7 +169,7 @@ impl ApplicationRelatedData {
|
|||
|
||||
/// Generation dates/times of key pairs
|
||||
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 {
|
||||
let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?;
|
||||
|
@ -185,7 +185,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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
|
||||
|
||||
|
@ -193,7 +193,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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 {
|
||||
None => Ok(None),
|
||||
|
@ -202,7 +202,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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 {
|
||||
None => Ok(None),
|
||||
|
@ -211,7 +211,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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 {
|
||||
None => Ok(None),
|
||||
|
@ -220,7 +220,7 @@ impl ApplicationRelatedData {
|
|||
}
|
||||
|
||||
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 {
|
||||
None => Ok(None),
|
||||
|
|
|
@ -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
|
||||
|
||||
//! Cardholder Related Data (see spec pg. 22)
|
||||
|
@ -7,6 +7,7 @@ use std::convert::TryFrom;
|
|||
|
||||
use crate::card_do::{CardholderRelatedData, Lang, Sex};
|
||||
use crate::tlv::{value::Value, Tlv};
|
||||
use crate::Tags;
|
||||
|
||||
impl CardholderRelatedData {
|
||||
pub fn name(&self) -> Option<&[u8]> {
|
||||
|
@ -35,11 +36,11 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
|
|||
|
||||
fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
|
||||
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()
|
||||
.chunks(2)
|
||||
.map(|c| match c.len() {
|
||||
|
@ -51,7 +52,7 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
|
|||
});
|
||||
|
||||
let sex = tlv
|
||||
.find(&[0x5f, 0x35].into())
|
||||
.find(Tags::Sex)
|
||||
.map(|v| v.serialize())
|
||||
.filter(|v| v.len() == 1)
|
||||
.map(|v| Sex::from(v[0]));
|
||||
|
|
|
@ -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
|
||||
|
||||
//! Generate and import keys
|
||||
|
@ -16,7 +16,7 @@ use crate::crypto_data::{
|
|||
};
|
||||
use crate::openpgp::OpenPgpTransaction;
|
||||
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.
|
||||
///
|
||||
|
@ -92,10 +92,10 @@ pub(crate) fn gen_key_with_metadata(
|
|||
|
||||
/// Transform a public key Tlv from the card into PublicKeyMaterial
|
||||
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> {
|
||||
let n = tlv.find(&[0x81].into());
|
||||
let v = tlv.find(&[0x82].into());
|
||||
let n = tlv.find(Tag::from([0x81]));
|
||||
let v = tlv.find(Tag::from([0x82]));
|
||||
|
||||
let ec = tlv.find(&[0x86].into());
|
||||
let ec = tlv.find(Tag::from([0x86]));
|
||||
|
||||
match (n, v, ec) {
|
||||
(Some(n), Some(v), None) => {
|
||||
|
@ -454,14 +454,14 @@ fn rsa_key_import_cmd(
|
|||
}
|
||||
|
||||
// Assemble the DOs for upload
|
||||
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data));
|
||||
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
|
||||
let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
|
||||
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
|
||||
|
||||
// "Control Reference Template"
|
||||
let crt = control_reference_template(key_type)?;
|
||||
|
||||
// "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
|
||||
Ok(commands::key_import(ehl.serialize().to_vec()))
|
||||
|
@ -505,16 +505,16 @@ fn ecc_key_import_cmd(
|
|||
// Assemble DOs
|
||||
|
||||
// "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"
|
||||
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
|
||||
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
|
||||
|
||||
// "Control Reference Template"
|
||||
let crt = control_reference_template(key_type)?;
|
||||
|
||||
// "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
|
||||
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> {
|
||||
// "Control Reference Template" (0xB8 | 0xB6 | 0xA4)
|
||||
let tag = match key_type {
|
||||
KeyType::Decryption => 0xB8,
|
||||
KeyType::Signing => 0xB6,
|
||||
KeyType::Authentication => 0xA4,
|
||||
KeyType::Decryption => Tags::CrtKeyConfidentiality,
|
||||
KeyType::Signing => Tags::CrtKeySignature,
|
||||
KeyType::Authentication => Tags::CrtKeyAuthentication,
|
||||
_ => return Err(Error::InternalError("Unexpected KeyType".to_string())),
|
||||
};
|
||||
Ok(Tlv::new([tag], Value::S(vec![])))
|
||||
Ok(Tlv::new(tag, Value::S(vec![])))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
//! Client library for
|
||||
|
@ -25,6 +25,8 @@
|
|||
//! crate offers a higher level wrapper based on the
|
||||
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
|
||||
|
||||
extern crate core;
|
||||
|
||||
pub mod algorithm;
|
||||
pub(crate) mod apdu;
|
||||
pub mod card_do;
|
||||
|
@ -117,7 +119,10 @@ pub trait CardTransaction {
|
|||
|
||||
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.
|
||||
|
@ -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)]
|
||||
pub enum PinType {
|
||||
Sign,
|
||||
|
@ -270,39 +434,42 @@ pub enum KeyType {
|
|||
|
||||
impl KeyType {
|
||||
/// 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 {
|
||||
Self::Signing => 0xC1,
|
||||
Self::Decryption => 0xC2,
|
||||
Self::Authentication => 0xC3,
|
||||
Self::Attestation => 0xDA,
|
||||
Self::Signing => Tags::AlgorithmAttributesSignature,
|
||||
Self::Decryption => Tags::AlgorithmAttributesDecryption,
|
||||
Self::Authentication => Tags::AlgorithmAttributesAuthentication,
|
||||
Self::Attestation => Tags::AlgorithmAttributesAttestation,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Get C7/C8/C9/DB values for this KeyTypes, to use as Tag.
|
||||
///
|
||||
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
||||
/// fingerprint information from the card uses the combined Tag C5)
|
||||
fn fingerprint_put_tag(&self) -> u8 {
|
||||
fn fingerprint_put_tag(&self) -> Tag {
|
||||
match self {
|
||||
Self::Signing => 0xC7,
|
||||
Self::Decryption => 0xC8,
|
||||
Self::Authentication => 0xC9,
|
||||
Self::Attestation => 0xDB,
|
||||
Self::Signing => Tags::FingerprintSignature,
|
||||
Self::Decryption => Tags::FingerprintDecryption,
|
||||
Self::Authentication => Tags::FingerprintAuthentication,
|
||||
Self::Attestation => Tags::FingerprintAttestation,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Get CE/CF/D0/DD values for this KeyTypes, to use as Tag.
|
||||
///
|
||||
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
||||
/// timestamp information from the card uses the combined Tag CD)
|
||||
fn timestamp_put_tag(&self) -> u8 {
|
||||
fn timestamp_put_tag(&self) -> Tag {
|
||||
match self {
|
||||
Self::Signing => 0xCE,
|
||||
Self::Decryption => 0xCF,
|
||||
Self::Authentication => 0xD0,
|
||||
Self::Attestation => 0xDD,
|
||||
Self::Signing => Tags::GenerationTimeSignature,
|
||||
Self::Decryption => Tags::GenerationTimeDecryption,
|
||||
Self::Authentication => Tags::GenerationTimeAuthentication,
|
||||
Self::Attestation => Tags::GenerationTimeAttestation,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial}
|
|||
use crate::tlv::{value::Value, Tlv};
|
||||
use crate::{
|
||||
apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes,
|
||||
Tag, Tags,
|
||||
};
|
||||
|
||||
/// An OpenPGP card access object, backed by a CardBackend implementation.
|
||||
|
@ -116,7 +117,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
resp.check_ok()?;
|
||||
|
||||
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())
|
||||
})?;
|
||||
|
||||
|
@ -192,8 +193,8 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
log::info!("OpenPgpTransaction: select_data");
|
||||
|
||||
let tlv = Tlv::new(
|
||||
[0x60],
|
||||
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
|
||||
Tags::GeneralReference,
|
||||
Value::C(vec![Tlv::new(Tags::TagList, Value::S(tag.to_vec()))]),
|
||||
);
|
||||
|
||||
let data = tlv.serialize();
|
||||
|
@ -487,13 +488,13 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
// -> 86 xx 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
|
||||
let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk]));
|
||||
let pkdo = Tlv::new(Tags::PublicKey, Value::C(vec![epk]));
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
@ -652,7 +653,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
log::info!("OpenPgpTransaction: set_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()
|
||||
}
|
||||
|
@ -684,7 +685,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_fingerprint(&mut self, fp: Fingerprint, key_type: KeyType) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
@ -692,21 +693,21 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_ca_fingerprint_1(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
pub fn set_ca_fingerprint_2(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
pub fn set_ca_fingerprint_3(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -726,7 +727,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
.copied()
|
||||
.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()
|
||||
}
|
||||
|
@ -740,7 +741,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_resetting_code(&mut self, resetting_code: &[u8]) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -750,7 +751,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -758,7 +759,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -766,7 +767,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -870,20 +871,20 @@ fn digestinfo(hash: Hash) -> Vec<u8> {
|
|||
match hash {
|
||||
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
|
||||
let tlv = Tlv::new(
|
||||
[0x30],
|
||||
Tags::Sequence,
|
||||
Value::C(vec![
|
||||
Tlv::new(
|
||||
[0x30],
|
||||
Tags::Sequence,
|
||||
Value::C(vec![
|
||||
Tlv::new(
|
||||
[0x06],
|
||||
Tags::ObjectIdentifier,
|
||||
// unwrapping is ok, for SHA*
|
||||
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())),
|
||||
]),
|
||||
);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
pub(crate) mod length;
|
||||
|
@ -28,13 +28,14 @@ impl Tlv {
|
|||
}
|
||||
|
||||
/// Find the first occurrence of `tag` and return its value (if any)
|
||||
pub fn find(&self, tag: &Tag) -> Option<&Value> {
|
||||
if &self.tag == tag {
|
||||
pub fn find<T: Clone + Into<Tag>>(&self, tag: T) -> Option<&Value> {
|
||||
let t: Tag = tag.clone().into();
|
||||
if self.tag == t {
|
||||
Some(&self.value)
|
||||
} else {
|
||||
if let Value::C(inner) = &self.value {
|
||||
for tlv in inner {
|
||||
if let Some(found) = tlv.find(tag) {
|
||||
if let Some(found) = tlv.find(tag.clone()) {
|
||||
return Some(found);
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +85,12 @@ mod test {
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use super::{Tlv, Value};
|
||||
use crate::Error;
|
||||
use crate::{Error, Tags};
|
||||
|
||||
#[test]
|
||||
fn test_tlv0() {
|
||||
let cpkt = Tlv::new(
|
||||
[0x7F, 0x48],
|
||||
Tags::CardholderPrivateKeyTemplate,
|
||||
Value::S(vec![
|
||||
0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00,
|
||||
]),
|
||||
|
@ -109,16 +110,22 @@ mod test {
|
|||
|
||||
assert_eq!(
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
|
||||
|
@ -136,69 +143,69 @@ mod test {
|
|||
assert_eq!(serialized, data.to_vec());
|
||||
|
||||
// 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(),
|
||||
hex!("4f10d27600012401030400061601918000005f520800730000e00590007f740381012073820110c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
|
||||
|
||||
// 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"));
|
||||
|
||||
let value = tlv.find(&[0x4f].into()).unwrap();
|
||||
let value = tlv.find(Tags::ApplicationIdentifier).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0x7f, 0x74].into()).unwrap();
|
||||
let value = tlv.find(Tags::GeneralFeatureManagement).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xc0].into()).unwrap();
|
||||
let value = tlv.find(Tags::ExtendedCapabilities).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xc2].into()).unwrap();
|
||||
let value = tlv.find(Tags::AlgorithmAttributesDecryption).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xda].into()).unwrap();
|
||||
let value = tlv.find(Tags::AlgorithmAttributesAttestation).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xc5].into()).unwrap();
|
||||
let value = tlv.find(Tags::Fingerprints).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xcd].into()).unwrap();
|
||||
let value = tlv.find(Tags::GenerationTimes).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0x7f, 0x66].into()).unwrap();
|
||||
let value = tlv.find(Tags::ExtendedLengthInformation).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xd7].into()).unwrap();
|
||||
let value = tlv.find(Tags::UifDec).unwrap();
|
||||
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"));
|
||||
|
||||
let value = tlv.find(&[0xd9].into()).unwrap();
|
||||
let value = tlv.find(Tags::UifAttestation).unwrap();
|
||||
assert_eq!(value.serialize(), hex!("0020"));
|
||||
|
||||
Ok(())
|
||||
|
@ -210,11 +217,14 @@ mod test {
|
|||
// but has been abridged and changed. It does not represent a
|
||||
// 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!(
|
||||
tlv.serialize(),
|
||||
|
|
Loading…
Reference in a new issue