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
//! 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 (tag consists of two bytes)
fn get_data2(tag0: u8, tag1: u8) -> Command {
Command::new(0x00, 0xCA, tag0, tag1, 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
}
}
/// 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)

View file

@ -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),

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
//! 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]));

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
//! 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![])))
}

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
//! 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()
}
}

View file

@ -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())),
]),
);

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
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(),