From 7854a40b5b95a3e81660fb439e9af8b2d7f20042 Mon Sep 17 00:00:00 2001 From: Heiko Schaefer Date: Wed, 4 May 2022 11:43:39 +0200 Subject: [PATCH] Replace hard coded tag values with Tags enum. Fixes #12 --- openpgp-card/src/apdu/commands.rs | 74 +++++---- openpgp-card/src/card_do.rs | 30 ++-- openpgp-card/src/card_do/cardholder.rs | 11 +- openpgp-card/src/keys.rs | 30 ++-- openpgp-card/src/lib.rs | 201 ++++++++++++++++++++++--- openpgp-card/src/openpgp.rs | 43 +++--- openpgp-card/src/tlv.rs | 76 ++++++---- 7 files changed, 326 insertions(+), 139 deletions(-) diff --git a/openpgp-card/src/apdu/commands.rs b/openpgp-card/src/apdu/commands.rs index e601904..58b4727 100644 --- a/openpgp-card/src/apdu/commands.rs +++ b/openpgp-card/src/apdu/commands.rs @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer // 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>(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) -> Command { } /// 7.2.8 PUT DATA, -/// ('tag' must consist of either one or two bytes) -pub(crate) fn put_data(tag: &[u8], data: Vec) -> 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>(tag: T, data: Vec) -> 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) -> 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) -> Command { - put_data(&[0x5b], name) + put_data(Tags::Name, name) } /// PUT DO Language preferences pub(crate) fn put_lang(lang: Vec) -> 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) -> Command { - put_data(&[0x5f, 0x50], url) + put_data(Tags::Url, url) } /// PUT DO "PW status bytes" pub(crate) fn put_pw_status(data: Vec) -> Command { - put_data(&[0xc4], data) + put_data(Tags::PWStatusBytes, data) } /// PUT DO "Cardholder certificate" pub(crate) fn put_cardholder_certificate(data: Vec) -> Command { - put_data(&[0x7F, 0x21], data) + put_data(Tags::CardholderCertificate, data) } /// "RESET RETRY COUNTER" (PW1, user pin) diff --git a/openpgp-card/src/card_do.rs b/openpgp-card/src/card_do.rs index 28b5546..9a72d7b 100644 --- a/openpgp-card/src/card_do.rs +++ b/openpgp-card/src/card_do.rs @@ -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 { - 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 { - 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, 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 { - 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 { - 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, Error> { - let fp = self.0.find(&[0xc5].into()); + let fp = self.0.find(Tags::Fingerprints); if let Some(fp) = fp { let fp: KeySet = (&fp.serialize()[..]).try_into()?; @@ -151,7 +151,7 @@ impl ApplicationRelatedData { } pub fn ca_fingerprints(&self) -> Result<[Option; 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, crate::Error> { - let kg = self.0.find(&[0xcd].into()); + let kg = self.0.find(Tags::GenerationTimes); if let Some(kg) = kg { let kg: KeySet = (&kg.serialize()[..]).try_into()?; @@ -185,7 +185,7 @@ impl ApplicationRelatedData { } pub fn key_information(&self) -> Result, 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, 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, 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, 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, Error> { - let uif = self.0.find(&[0xd9].into()); + let uif = self.0.find(Tags::UifAttestation); match uif { None => Ok(None), diff --git a/openpgp-card/src/card_do/cardholder.rs b/openpgp-card/src/card_do/cardholder.rs index 55d50a2..4a332ac 100644 --- a/openpgp-card/src/card_do/cardholder.rs +++ b/openpgp-card/src/card_do/cardholder.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer // 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 { let value = Value::from(data, true)?; - let tlv = Tlv::new([0x65], value); + let tlv = Tlv::new(Tags::CardholderRelatedData, value); - let name: Option> = tlv.find(&[0x5b].into()).map(|v| v.serialize().to_vec()); + let name: Option> = tlv.find(Tags::Name).map(|v| v.serialize().to_vec()); - let lang: Option> = tlv.find(&[0x5f, 0x2d].into()).map(|v| { + let lang: Option> = 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])); diff --git a/openpgp-card/src/keys.rs b/openpgp-card/src/keys.rs index 4ba8afd..9f73a95 100644 --- a/openpgp-card/src/keys.rs +++ b/openpgp-card/src/keys.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer // 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 { - 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 { // "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![]))) } diff --git a/openpgp-card/src/lib.rs b/openpgp-card/src/lib.rs index eebddef..780a82e 100644 --- a/openpgp-card/src/lib.rs +++ b/openpgp-card/src/lib.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer // 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 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() } } diff --git a/openpgp-card/src/openpgp.rs b/openpgp-card/src/openpgp.rs index 69275b6..5124ec2 100644 --- a/openpgp-card/src/openpgp.rs +++ b/openpgp-card/src/openpgp.rs @@ -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 { 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())), ]), ); diff --git a/openpgp-card/src/tlv.rs b/openpgp-card/src/tlv.rs index 61e86ce..2a72fbb 100644 --- a/openpgp-card/src/tlv.rs +++ b/openpgp-card/src/tlv.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2021 Heiko Schaefer +// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer // 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>(&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(),