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
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
//! Pre-defined `Command` values for the OpenPGP card application
|
//! Pre-defined `Command` values for the OpenPGP card application
|
||||||
|
|
||||||
use crate::apdu::command::Command;
|
use crate::apdu::command::Command;
|
||||||
|
use crate::{Tag, Tags};
|
||||||
|
|
||||||
/// 7.2.1 SELECT
|
/// 7.2.1 SELECT
|
||||||
/// (select the OpenPGP application on the card)
|
/// (select the OpenPGP application on the card)
|
||||||
|
@ -17,49 +18,54 @@ pub(crate) fn select_openpgp() -> Command {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 7.2.6 GET DATA (tag consists one byte)
|
/// 7.2.6 GET DATA
|
||||||
fn get_data1(tag0: u8) -> Command {
|
fn get_data<T: Into<Tag>>(tag: T) -> Command {
|
||||||
Command::new(0x00, 0xCA, 0, tag0, vec![])
|
match *tag.into().get() {
|
||||||
}
|
[tag0] => Command::new(0x00, 0xCA, 0, tag0, vec![]),
|
||||||
|
[tag0, tag1] => Command::new(0x00, 0xCA, tag0, tag1, vec![]),
|
||||||
/// 7.2.6 GET DATA (tag consists of two bytes)
|
_ => panic!("this should never happen"), // FIXME
|
||||||
fn get_data2(tag0: u8, tag1: u8) -> Command {
|
}
|
||||||
Command::new(0x00, 0xCA, tag0, tag1, vec![])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Application related data"
|
/// GET DO "Application related data"
|
||||||
pub(crate) fn application_related_data() -> Command {
|
pub(crate) fn application_related_data() -> Command {
|
||||||
get_data1(0x6E)
|
get_data(Tags::ApplicationRelatedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "private use"
|
/// GET DO "private use"
|
||||||
pub(crate) fn private_use_do(num: u8) -> Command {
|
pub(crate) fn private_use_do(num: u8) -> Command {
|
||||||
get_data2(0x01, num)
|
match num {
|
||||||
|
1 => get_data(Tags::PrivateUse1),
|
||||||
|
2 => get_data(Tags::PrivateUse2),
|
||||||
|
3 => get_data(Tags::PrivateUse3),
|
||||||
|
4 => get_data(Tags::PrivateUse4),
|
||||||
|
_ => panic!("this should never happen"), // FIXME
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Uniform resource locator"
|
/// GET DO "Uniform resource locator"
|
||||||
pub(crate) fn url() -> Command {
|
pub(crate) fn url() -> Command {
|
||||||
get_data2(0x5F, 0x50)
|
get_data(Tags::Url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Cardholder related data"
|
/// GET DO "Cardholder related data"
|
||||||
pub(crate) fn cardholder_related_data() -> Command {
|
pub(crate) fn cardholder_related_data() -> Command {
|
||||||
get_data1(0x65)
|
get_data(Tags::CardholderRelatedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Security support template"
|
/// GET DO "Security support template"
|
||||||
pub(crate) fn security_support_template() -> Command {
|
pub(crate) fn security_support_template() -> Command {
|
||||||
get_data1(0x7A)
|
get_data(Tags::SecuritySupportTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Cardholder certificate"
|
/// GET DO "Cardholder certificate"
|
||||||
pub(crate) fn cardholder_certificate() -> Command {
|
pub(crate) fn cardholder_certificate() -> Command {
|
||||||
get_data2(0x7F, 0x21)
|
get_data(Tags::CardholderCertificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET DO "Algorithm Information"
|
/// GET DO "Algorithm Information"
|
||||||
pub(crate) fn algo_info() -> Command {
|
pub(crate) fn algo_info() -> Command {
|
||||||
get_data1(0xFA)
|
get_data(Tags::AlgorithmInformation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GET Firmware Version (yubikey specific?)
|
/// GET Firmware Version (yubikey specific?)
|
||||||
|
@ -98,51 +104,53 @@ pub(crate) fn verify_pw3(pin: Vec<u8>) -> Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 7.2.8 PUT DATA,
|
/// 7.2.8 PUT DATA,
|
||||||
/// ('tag' must consist of either one or two bytes)
|
pub(crate) fn put_data<T: Into<Tag>>(tag: T, data: Vec<u8>) -> Command {
|
||||||
pub(crate) fn put_data(tag: &[u8], data: Vec<u8>) -> Command {
|
match *tag.into().get() {
|
||||||
assert!(!tag.is_empty() && tag.len() <= 2);
|
[tag0] => Command::new(0x00, 0xda, 0, tag0, data),
|
||||||
|
[tag0, tag1] => Command::new(0x00, 0xda, tag0, tag1, data),
|
||||||
let (p1, p2) = if tag.len() == 2 {
|
_ => panic!("this should never happen"), // FIXME
|
||||||
(tag[0], tag[1])
|
}
|
||||||
} else {
|
|
||||||
(0, tag[0])
|
|
||||||
};
|
|
||||||
Command::new(0x00, 0xda, p1, p2, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO "private use"
|
/// PUT DO "private use"
|
||||||
pub(crate) fn put_private_use_do(num: u8, data: Vec<u8>) -> Command {
|
pub(crate) fn put_private_use_do(num: u8, data: Vec<u8>) -> Command {
|
||||||
put_data(&[0x01, num], data)
|
match num {
|
||||||
|
1 => put_data(Tags::PrivateUse1, data),
|
||||||
|
2 => put_data(Tags::PrivateUse2, data),
|
||||||
|
3 => put_data(Tags::PrivateUse3, data),
|
||||||
|
4 => put_data(Tags::PrivateUse4, data),
|
||||||
|
_ => panic!("this should never happen"), // FIXME
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO Name
|
/// PUT DO Name
|
||||||
pub(crate) fn put_name(name: Vec<u8>) -> Command {
|
pub(crate) fn put_name(name: Vec<u8>) -> Command {
|
||||||
put_data(&[0x5b], name)
|
put_data(Tags::Name, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO Language preferences
|
/// PUT DO Language preferences
|
||||||
pub(crate) fn put_lang(lang: Vec<u8>) -> Command {
|
pub(crate) fn put_lang(lang: Vec<u8>) -> Command {
|
||||||
put_data(&[0x5f, 0x2d], lang)
|
put_data(Tags::LanguagePref, lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO Sex
|
/// PUT DO Sex
|
||||||
pub(crate) fn put_sex(sex: u8) -> Command {
|
pub(crate) fn put_sex(sex: u8) -> Command {
|
||||||
put_data(&[0x5f, 0x35], vec![sex])
|
put_data(Tags::Sex, vec![sex])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO Uniform resource locator (URL)
|
/// PUT DO Uniform resource locator (URL)
|
||||||
pub(crate) fn put_url(url: Vec<u8>) -> Command {
|
pub(crate) fn put_url(url: Vec<u8>) -> Command {
|
||||||
put_data(&[0x5f, 0x50], url)
|
put_data(Tags::Url, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO "PW status bytes"
|
/// PUT DO "PW status bytes"
|
||||||
pub(crate) fn put_pw_status(data: Vec<u8>) -> Command {
|
pub(crate) fn put_pw_status(data: Vec<u8>) -> Command {
|
||||||
put_data(&[0xc4], data)
|
put_data(Tags::PWStatusBytes, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT DO "Cardholder certificate"
|
/// PUT DO "Cardholder certificate"
|
||||||
pub(crate) fn put_cardholder_certificate(data: Vec<u8>) -> Command {
|
pub(crate) fn put_cardholder_certificate(data: Vec<u8>) -> Command {
|
||||||
put_data(&[0x7F, 0x21], data)
|
put_data(Tags::CardholderCertificate, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "RESET RETRY COUNTER" (PW1, user pin)
|
/// "RESET RETRY COUNTER" (PW1, user pin)
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::convert::{TryFrom, TryInto};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
|
|
||||||
use crate::{algorithm::Algo, tlv::Tlv, Error, KeySet, KeyType};
|
use crate::{algorithm::Algo, tlv::Tlv, Error, KeySet, KeyType, Tags};
|
||||||
|
|
||||||
mod algo_attrs;
|
mod algo_attrs;
|
||||||
mod algo_info;
|
mod algo_info;
|
||||||
|
@ -34,7 +34,7 @@ pub struct ApplicationRelatedData(pub(crate) Tlv);
|
||||||
impl ApplicationRelatedData {
|
impl ApplicationRelatedData {
|
||||||
/// Get application identifier (AID), ISO 7816-4
|
/// Get application identifier (AID), ISO 7816-4
|
||||||
pub fn application_id(&self) -> Result<ApplicationIdentifier, Error> {
|
pub fn application_id(&self) -> Result<ApplicationIdentifier, Error> {
|
||||||
let aid = self.0.find(&[0x4f].into());
|
let aid = self.0.find(Tags::ApplicationIdentifier);
|
||||||
|
|
||||||
if let Some(aid) = aid {
|
if let Some(aid) = aid {
|
||||||
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
||||||
|
@ -45,7 +45,7 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
/// Get historical bytes
|
/// Get historical bytes
|
||||||
pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> {
|
pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> {
|
||||||
let hist = self.0.find(&[0x5f, 0x52].into());
|
let hist = self.0.find(Tags::HistoricalBytes);
|
||||||
|
|
||||||
if let Some(hist) = hist {
|
if let Some(hist) = hist {
|
||||||
log::trace!("Historical bytes: {:x?}", hist);
|
log::trace!("Historical bytes: {:x?}", hist);
|
||||||
|
@ -60,7 +60,7 @@ impl ApplicationRelatedData {
|
||||||
/// Get extended length information (ISO 7816-4), which
|
/// Get extended length information (ISO 7816-4), which
|
||||||
/// contains maximum number of bytes for command and response.
|
/// contains maximum number of bytes for command and response.
|
||||||
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
|
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
|
||||||
let eli = self.0.find(&[0x7f, 0x66].into());
|
let eli = self.0.find(Tags::ExtendedLengthInformation);
|
||||||
|
|
||||||
log::trace!("Extended length information: {:x?}", eli);
|
log::trace!("Extended length information: {:x?}", eli);
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ impl ApplicationRelatedData {
|
||||||
let version = app_id.version();
|
let version = app_id.version();
|
||||||
|
|
||||||
// get from cached "application related data"
|
// get from cached "application related data"
|
||||||
let ecap = self.0.find(&[0xc0].into());
|
let ecap = self.0.find(Tags::ExtendedCapabilities);
|
||||||
|
|
||||||
if let Some(ecap) = ecap {
|
if let Some(ecap) = ecap {
|
||||||
Ok(ExtendedCapabilities::try_from((
|
Ok(ExtendedCapabilities::try_from((
|
||||||
|
@ -105,7 +105,7 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
/// Get algorithm attributes (for each key type)
|
/// Get algorithm attributes (for each key type)
|
||||||
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
|
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
|
||||||
let aa = self.0.find(&[key_type.algorithm_tag()].into());
|
let aa = self.0.find(key_type.algorithm_tag());
|
||||||
|
|
||||||
if let Some(aa) = aa {
|
if let Some(aa) = aa {
|
||||||
Algo::try_from(&aa.serialize()[..])
|
Algo::try_from(&aa.serialize()[..])
|
||||||
|
@ -119,7 +119,7 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
/// Get PW status Bytes
|
/// Get PW status Bytes
|
||||||
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
|
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
|
||||||
let psb = self.0.find(&[0xc4].into());
|
let psb = self.0.find(Tags::PWStatusBytes);
|
||||||
|
|
||||||
if let Some(psb) = psb {
|
if let Some(psb) = psb {
|
||||||
let pws = (&psb.serialize()[..]).try_into()?;
|
let pws = (&psb.serialize()[..]).try_into()?;
|
||||||
|
@ -137,7 +137,7 @@ impl ApplicationRelatedData {
|
||||||
/// Fingerprint, per key type.
|
/// Fingerprint, per key type.
|
||||||
/// Zero bytes indicate a not defined private key.
|
/// Zero bytes indicate a not defined private key.
|
||||||
pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> {
|
pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> {
|
||||||
let fp = self.0.find(&[0xc5].into());
|
let fp = self.0.find(Tags::Fingerprints);
|
||||||
|
|
||||||
if let Some(fp) = fp {
|
if let Some(fp) = fp {
|
||||||
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
|
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
|
||||||
|
@ -151,7 +151,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> {
|
pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> {
|
||||||
let fp = self.0.find(&[0xc6].into());
|
let fp = self.0.find(Tags::CaFingerprints);
|
||||||
|
|
||||||
if let Some(fp) = fp {
|
if let Some(fp) = fp {
|
||||||
// FIXME: using a KeySet is a weird hack
|
// FIXME: using a KeySet is a weird hack
|
||||||
|
@ -169,7 +169,7 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
/// Generation dates/times of key pairs
|
/// Generation dates/times of key pairs
|
||||||
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> {
|
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> {
|
||||||
let kg = self.0.find(&[0xcd].into());
|
let kg = self.0.find(Tags::GenerationTimes);
|
||||||
|
|
||||||
if let Some(kg) = kg {
|
if let Some(kg) = kg {
|
||||||
let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?;
|
let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?;
|
||||||
|
@ -185,7 +185,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> {
|
pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> {
|
||||||
let ki = self.0.find(&[0xde].into());
|
let ki = self.0.find(Tags::KeyInformation);
|
||||||
|
|
||||||
// TODO: return an error in .into(), if the format of the value is bad
|
// TODO: return an error in .into(), if the format of the value is bad
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uif_pso_cds(&self) -> Result<Option<UIF>, Error> {
|
pub fn uif_pso_cds(&self) -> Result<Option<UIF>, Error> {
|
||||||
let uif = self.0.find(&[0xd6].into());
|
let uif = self.0.find(Tags::UifSig);
|
||||||
|
|
||||||
match uif {
|
match uif {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -202,7 +202,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uif_pso_dec(&self) -> Result<Option<UIF>, Error> {
|
pub fn uif_pso_dec(&self) -> Result<Option<UIF>, Error> {
|
||||||
let uif = self.0.find(&[0xd7].into());
|
let uif = self.0.find(Tags::UifDec);
|
||||||
|
|
||||||
match uif {
|
match uif {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -211,7 +211,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uif_pso_aut(&self) -> Result<Option<UIF>, Error> {
|
pub fn uif_pso_aut(&self) -> Result<Option<UIF>, Error> {
|
||||||
let uif = self.0.find(&[0xd8].into());
|
let uif = self.0.find(Tags::UifAuth);
|
||||||
|
|
||||||
match uif {
|
match uif {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -220,7 +220,7 @@ impl ApplicationRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
|
pub fn uif_attestation(&self) -> Result<Option<UIF>, Error> {
|
||||||
let uif = self.0.find(&[0xd9].into());
|
let uif = self.0.find(Tags::UifAttestation);
|
||||||
|
|
||||||
match uif {
|
match uif {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
//! Cardholder Related Data (see spec pg. 22)
|
//! Cardholder Related Data (see spec pg. 22)
|
||||||
|
@ -7,6 +7,7 @@ use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::card_do::{CardholderRelatedData, Lang, Sex};
|
use crate::card_do::{CardholderRelatedData, Lang, Sex};
|
||||||
use crate::tlv::{value::Value, Tlv};
|
use crate::tlv::{value::Value, Tlv};
|
||||||
|
use crate::Tags;
|
||||||
|
|
||||||
impl CardholderRelatedData {
|
impl CardholderRelatedData {
|
||||||
pub fn name(&self) -> Option<&[u8]> {
|
pub fn name(&self) -> Option<&[u8]> {
|
||||||
|
@ -35,11 +36,11 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
|
fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
|
||||||
let value = Value::from(data, true)?;
|
let value = Value::from(data, true)?;
|
||||||
let tlv = Tlv::new([0x65], value);
|
let tlv = Tlv::new(Tags::CardholderRelatedData, value);
|
||||||
|
|
||||||
let name: Option<Vec<u8>> = tlv.find(&[0x5b].into()).map(|v| v.serialize().to_vec());
|
let name: Option<Vec<u8>> = tlv.find(Tags::Name).map(|v| v.serialize().to_vec());
|
||||||
|
|
||||||
let lang: Option<Vec<Lang>> = tlv.find(&[0x5f, 0x2d].into()).map(|v| {
|
let lang: Option<Vec<Lang>> = tlv.find(Tags::LanguagePref).map(|v| {
|
||||||
v.serialize()
|
v.serialize()
|
||||||
.chunks(2)
|
.chunks(2)
|
||||||
.map(|c| match c.len() {
|
.map(|c| match c.len() {
|
||||||
|
@ -51,7 +52,7 @@ impl TryFrom<&[u8]> for CardholderRelatedData {
|
||||||
});
|
});
|
||||||
|
|
||||||
let sex = tlv
|
let sex = tlv
|
||||||
.find(&[0x5f, 0x35].into())
|
.find(Tags::Sex)
|
||||||
.map(|v| v.serialize())
|
.map(|v| v.serialize())
|
||||||
.filter(|v| v.len() == 1)
|
.filter(|v| v.len() == 1)
|
||||||
.map(|v| Sex::from(v[0]));
|
.map(|v| Sex::from(v[0]));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
//! Generate and import keys
|
//! Generate and import keys
|
||||||
|
@ -16,7 +16,7 @@ use crate::crypto_data::{
|
||||||
};
|
};
|
||||||
use crate::openpgp::OpenPgpTransaction;
|
use crate::openpgp::OpenPgpTransaction;
|
||||||
use crate::tlv::{length::tlv_encode_length, value::Value, Tlv};
|
use crate::tlv::{length::tlv_encode_length, value::Value, Tlv};
|
||||||
use crate::{apdu, Error, KeyType};
|
use crate::{apdu, Error, KeyType, Tag, Tags};
|
||||||
|
|
||||||
/// Generate asymmetric key pair on the card.
|
/// Generate asymmetric key pair on the card.
|
||||||
///
|
///
|
||||||
|
@ -92,10 +92,10 @@ pub(crate) fn gen_key_with_metadata(
|
||||||
|
|
||||||
/// Transform a public key Tlv from the card into PublicKeyMaterial
|
/// Transform a public key Tlv from the card into PublicKeyMaterial
|
||||||
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> {
|
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> {
|
||||||
let n = tlv.find(&[0x81].into());
|
let n = tlv.find(Tag::from([0x81]));
|
||||||
let v = tlv.find(&[0x82].into());
|
let v = tlv.find(Tag::from([0x82]));
|
||||||
|
|
||||||
let ec = tlv.find(&[0x86].into());
|
let ec = tlv.find(Tag::from([0x86]));
|
||||||
|
|
||||||
match (n, v, ec) {
|
match (n, v, ec) {
|
||||||
(Some(n), Some(v), None) => {
|
(Some(n), Some(v), None) => {
|
||||||
|
@ -454,14 +454,14 @@ fn rsa_key_import_cmd(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the DOs for upload
|
// Assemble the DOs for upload
|
||||||
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data));
|
let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
|
||||||
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
|
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
|
||||||
|
|
||||||
// "Control Reference Template"
|
// "Control Reference Template"
|
||||||
let crt = control_reference_template(key_type)?;
|
let crt = control_reference_template(key_type)?;
|
||||||
|
|
||||||
// "Extended header list (DO 4D)"
|
// "Extended header list (DO 4D)"
|
||||||
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk]));
|
let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
|
||||||
|
|
||||||
// Return the full key import command
|
// Return the full key import command
|
||||||
Ok(commands::key_import(ehl.serialize().to_vec()))
|
Ok(commands::key_import(ehl.serialize().to_vec()))
|
||||||
|
@ -505,16 +505,16 @@ fn ecc_key_import_cmd(
|
||||||
// Assemble DOs
|
// Assemble DOs
|
||||||
|
|
||||||
// "Cardholder private key template"
|
// "Cardholder private key template"
|
||||||
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data));
|
let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
|
||||||
|
|
||||||
// "Cardholder private key"
|
// "Cardholder private key"
|
||||||
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
|
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
|
||||||
|
|
||||||
// "Control Reference Template"
|
// "Control Reference Template"
|
||||||
let crt = control_reference_template(key_type)?;
|
let crt = control_reference_template(key_type)?;
|
||||||
|
|
||||||
// "Extended header list (DO 4D)" (contains the three inner TLV)
|
// "Extended header list (DO 4D)" (contains the three inner TLV)
|
||||||
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk]));
|
let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
|
||||||
|
|
||||||
// key import command
|
// key import command
|
||||||
Ok(commands::key_import(ehl.serialize().to_vec()))
|
Ok(commands::key_import(ehl.serialize().to_vec()))
|
||||||
|
@ -524,10 +524,10 @@ fn ecc_key_import_cmd(
|
||||||
fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> {
|
fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> {
|
||||||
// "Control Reference Template" (0xB8 | 0xB6 | 0xA4)
|
// "Control Reference Template" (0xB8 | 0xB6 | 0xA4)
|
||||||
let tag = match key_type {
|
let tag = match key_type {
|
||||||
KeyType::Decryption => 0xB8,
|
KeyType::Decryption => Tags::CrtKeyConfidentiality,
|
||||||
KeyType::Signing => 0xB6,
|
KeyType::Signing => Tags::CrtKeySignature,
|
||||||
KeyType::Authentication => 0xA4,
|
KeyType::Authentication => Tags::CrtKeyAuthentication,
|
||||||
_ => return Err(Error::InternalError("Unexpected KeyType".to_string())),
|
_ => return Err(Error::InternalError("Unexpected KeyType".to_string())),
|
||||||
};
|
};
|
||||||
Ok(Tlv::new([tag], Value::S(vec![])))
|
Ok(Tlv::new(tag, Value::S(vec![])))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
//! Client library for
|
//! Client library for
|
||||||
|
@ -25,6 +25,8 @@
|
||||||
//! crate offers a higher level wrapper based on the
|
//! crate offers a higher level wrapper based on the
|
||||||
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
|
//! [Sequoia PGP](https://sequoia-pgp.org/) implementation.
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
pub mod algorithm;
|
pub mod algorithm;
|
||||||
pub(crate) mod apdu;
|
pub(crate) mod apdu;
|
||||||
pub mod card_do;
|
pub mod card_do;
|
||||||
|
@ -117,7 +119,10 @@ pub trait CardTransaction {
|
||||||
|
|
||||||
log::trace!(" ARD value: {:x?}", value);
|
log::trace!(" ARD value: {:x?}", value);
|
||||||
|
|
||||||
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
|
Ok(ApplicationRelatedData(Tlv::new(
|
||||||
|
Tags::ApplicationRelatedData,
|
||||||
|
value,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a CardApp based on a CardTransaction.
|
/// Get a CardApp based on a CardTransaction.
|
||||||
|
@ -241,6 +246,165 @@ impl CardCaps {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tags, as specified and used in the OpenPGP card 3.4.1 spec.
|
||||||
|
/// All tags in OpenPGP card are either 1 or 2 bytes long.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) enum Tags {
|
||||||
|
// BER identifiers
|
||||||
|
OctetString,
|
||||||
|
Null,
|
||||||
|
ObjectIdentifier,
|
||||||
|
Sequence,
|
||||||
|
// GET DATA
|
||||||
|
PrivateUse1,
|
||||||
|
PrivateUse2,
|
||||||
|
PrivateUse3,
|
||||||
|
PrivateUse4,
|
||||||
|
ApplicationIdentifier,
|
||||||
|
LoginData,
|
||||||
|
Url,
|
||||||
|
HistoricalBytes,
|
||||||
|
CardholderRelatedData,
|
||||||
|
Name,
|
||||||
|
LanguagePref,
|
||||||
|
Sex,
|
||||||
|
ApplicationRelatedData,
|
||||||
|
ExtendedLengthInformation,
|
||||||
|
GeneralFeatureManagement,
|
||||||
|
DiscretionaryDataObjects,
|
||||||
|
ExtendedCapabilities,
|
||||||
|
AlgorithmAttributesSignature,
|
||||||
|
AlgorithmAttributesDecryption,
|
||||||
|
AlgorithmAttributesAuthentication,
|
||||||
|
PWStatusBytes,
|
||||||
|
Fingerprints,
|
||||||
|
CaFingerprints,
|
||||||
|
GenerationTimes,
|
||||||
|
KeyInformation,
|
||||||
|
UifSig,
|
||||||
|
UifDec,
|
||||||
|
UifAuth,
|
||||||
|
UifAttestation,
|
||||||
|
SecuritySupportTemplate,
|
||||||
|
DigitalSignatureCounter,
|
||||||
|
CardholderCertificate,
|
||||||
|
AlgorithmAttributesAttestation,
|
||||||
|
FingerprintAttestation,
|
||||||
|
CaFingerprintAttestation,
|
||||||
|
GenerationTimeAttestation,
|
||||||
|
KdfDo,
|
||||||
|
AlgorithmInformation,
|
||||||
|
CertificateSecureMessaging,
|
||||||
|
AttestationCertificate,
|
||||||
|
// PUT DATA (additional Tags that don't get used for GET DATA)
|
||||||
|
FingerprintSignature,
|
||||||
|
FingerprintDecryption,
|
||||||
|
FingerprintAuthentication,
|
||||||
|
CaFingerprint1,
|
||||||
|
CaFingerprint2,
|
||||||
|
CaFingerprint3,
|
||||||
|
GenerationTimeSignature,
|
||||||
|
GenerationTimeDecryption,
|
||||||
|
GenerationTimeAuthentication,
|
||||||
|
// FIXME: +D1, D2
|
||||||
|
ResettingCode,
|
||||||
|
// OTHER
|
||||||
|
// 4.4.3.12 Private Key Template
|
||||||
|
ExtendedHeaderList,
|
||||||
|
CardholderPrivateKeyTemplate,
|
||||||
|
ConcatenatedKeyData,
|
||||||
|
// 7.2.14 GENERATE ASYMMETRIC KEY PAIR
|
||||||
|
PublicKey,
|
||||||
|
// 7.2.11 PSO: DECIPHER
|
||||||
|
Cipher,
|
||||||
|
ExternalPublicKey,
|
||||||
|
// 7.2.5 SELECT DATA
|
||||||
|
GeneralReference,
|
||||||
|
TagList,
|
||||||
|
// 4.4.3.12 Private Key Template
|
||||||
|
CrtKeySignature,
|
||||||
|
CrtKeyConfidentiality,
|
||||||
|
CrtKeyAuthentication,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Tags> for Tag {
|
||||||
|
fn from(t: Tags) -> Self {
|
||||||
|
match t {
|
||||||
|
// BER identifiers https://en.wikipedia.org/wiki/X.690#BER_encoding
|
||||||
|
Tags::OctetString => [0x04].into(),
|
||||||
|
Tags::Null => [0x05].into(),
|
||||||
|
Tags::ObjectIdentifier => [0x06].into(),
|
||||||
|
Tags::Sequence => [0x30].into(),
|
||||||
|
// GET DATA
|
||||||
|
Tags::PrivateUse1 => [0x01, 0x01].into(),
|
||||||
|
Tags::PrivateUse2 => [0x01, 0x02].into(),
|
||||||
|
Tags::PrivateUse3 => [0x01, 0x03].into(),
|
||||||
|
Tags::PrivateUse4 => [0x01, 0x04].into(),
|
||||||
|
Tags::ApplicationIdentifier => [0x4f].into(),
|
||||||
|
Tags::LoginData => [0x5e].into(),
|
||||||
|
Tags::Url => [0x5f, 0x50].into(),
|
||||||
|
Tags::HistoricalBytes => [0x5f, 0x52].into(),
|
||||||
|
Tags::CardholderRelatedData => [0x65].into(),
|
||||||
|
Tags::Name => [0x5b].into(),
|
||||||
|
Tags::LanguagePref => [0x5f, 0x2d].into(),
|
||||||
|
Tags::Sex => [0x5f, 0x35].into(),
|
||||||
|
Tags::ApplicationRelatedData => [0x6e].into(),
|
||||||
|
Tags::ExtendedLengthInformation => [0x7f, 0x66].into(),
|
||||||
|
Tags::GeneralFeatureManagement => [0x7f, 0x74].into(),
|
||||||
|
Tags::DiscretionaryDataObjects => [0x73].into(),
|
||||||
|
Tags::ExtendedCapabilities => [0xc0].into(),
|
||||||
|
Tags::AlgorithmAttributesSignature => [0xc1].into(),
|
||||||
|
Tags::AlgorithmAttributesDecryption => [0xc2].into(),
|
||||||
|
Tags::AlgorithmAttributesAuthentication => [0xc3].into(),
|
||||||
|
Tags::PWStatusBytes => [0xc4].into(),
|
||||||
|
Tags::Fingerprints => [0xc5].into(),
|
||||||
|
Tags::CaFingerprints => [0xc6].into(),
|
||||||
|
Tags::GenerationTimes => [0xcd].into(),
|
||||||
|
Tags::KeyInformation => [0xde].into(),
|
||||||
|
Tags::UifSig => [0xd6].into(),
|
||||||
|
Tags::UifDec => [0xd7].into(),
|
||||||
|
Tags::UifAuth => [0xd8].into(),
|
||||||
|
Tags::UifAttestation => [0xd9].into(),
|
||||||
|
Tags::SecuritySupportTemplate => [0x7a].into(),
|
||||||
|
Tags::DigitalSignatureCounter => [0x93].into(),
|
||||||
|
Tags::CardholderCertificate => [0x7f, 0x21].into(),
|
||||||
|
Tags::AlgorithmAttributesAttestation => [0xda].into(),
|
||||||
|
Tags::FingerprintAttestation => [0xdb].into(),
|
||||||
|
Tags::CaFingerprintAttestation => [0xdc].into(),
|
||||||
|
Tags::GenerationTimeAttestation => [0xdd].into(),
|
||||||
|
Tags::KdfDo => [0xf9].into(),
|
||||||
|
Tags::AlgorithmInformation => [0xfa].into(),
|
||||||
|
Tags::CertificateSecureMessaging => [0xfb].into(),
|
||||||
|
Tags::AttestationCertificate => [0xfc].into(),
|
||||||
|
// PUT DATA
|
||||||
|
Tags::FingerprintSignature => [0xc7].into(),
|
||||||
|
Tags::FingerprintDecryption => [0xc8].into(),
|
||||||
|
Tags::FingerprintAuthentication => [0xc9].into(),
|
||||||
|
Tags::CaFingerprint1 => [0xca].into(),
|
||||||
|
Tags::CaFingerprint2 => [0xcb].into(),
|
||||||
|
Tags::CaFingerprint3 => [0xcc].into(),
|
||||||
|
Tags::GenerationTimeSignature => [0xce].into(),
|
||||||
|
Tags::GenerationTimeDecryption => [0xcf].into(),
|
||||||
|
Tags::GenerationTimeAuthentication => [0xd0].into(),
|
||||||
|
Tags::ResettingCode => [0xd3].into(),
|
||||||
|
// OTHER
|
||||||
|
Tags::ExtendedHeaderList => [0x4d].into(),
|
||||||
|
Tags::CardholderPrivateKeyTemplate => [0x7f, 0x48].into(),
|
||||||
|
Tags::ConcatenatedKeyData => [0x5f, 0x48].into(),
|
||||||
|
Tags::PublicKey => [0x7f, 0x49].into(),
|
||||||
|
Tags::Cipher => [0xa6].into(),
|
||||||
|
Tags::ExternalPublicKey => [0x86].into(),
|
||||||
|
Tags::GeneralReference => [0x60].into(),
|
||||||
|
Tags::TagList => [0x5c].into(),
|
||||||
|
Tags::CrtKeySignature => [0xb6].into(),
|
||||||
|
Tags::CrtKeyConfidentiality => [0xb8].into(),
|
||||||
|
Tags::CrtKeyAuthentication => [0xa4].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum PinType {
|
pub enum PinType {
|
||||||
Sign,
|
Sign,
|
||||||
|
@ -270,39 +434,42 @@ pub enum KeyType {
|
||||||
|
|
||||||
impl KeyType {
|
impl KeyType {
|
||||||
/// Get C1/C2/C3/DA values for this KeyTypes, to use as Tag
|
/// Get C1/C2/C3/DA values for this KeyTypes, to use as Tag
|
||||||
fn algorithm_tag(&self) -> u8 {
|
fn algorithm_tag(&self) -> Tag {
|
||||||
match self {
|
match self {
|
||||||
Self::Signing => 0xC1,
|
Self::Signing => Tags::AlgorithmAttributesSignature,
|
||||||
Self::Decryption => 0xC2,
|
Self::Decryption => Tags::AlgorithmAttributesDecryption,
|
||||||
Self::Authentication => 0xC3,
|
Self::Authentication => Tags::AlgorithmAttributesAuthentication,
|
||||||
Self::Attestation => 0xDA,
|
Self::Attestation => Tags::AlgorithmAttributesAttestation,
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get C7/C8/C9/DB values for this KeyTypes, to use as Tag.
|
/// Get C7/C8/C9/DB values for this KeyTypes, to use as Tag.
|
||||||
///
|
///
|
||||||
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
||||||
/// fingerprint information from the card uses the combined Tag C5)
|
/// fingerprint information from the card uses the combined Tag C5)
|
||||||
fn fingerprint_put_tag(&self) -> u8 {
|
fn fingerprint_put_tag(&self) -> Tag {
|
||||||
match self {
|
match self {
|
||||||
Self::Signing => 0xC7,
|
Self::Signing => Tags::FingerprintSignature,
|
||||||
Self::Decryption => 0xC8,
|
Self::Decryption => Tags::FingerprintDecryption,
|
||||||
Self::Authentication => 0xC9,
|
Self::Authentication => Tags::FingerprintAuthentication,
|
||||||
Self::Attestation => 0xDB,
|
Self::Attestation => Tags::FingerprintAttestation,
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get CE/CF/D0/DD values for this KeyTypes, to use as Tag.
|
/// Get CE/CF/D0/DD values for this KeyTypes, to use as Tag.
|
||||||
///
|
///
|
||||||
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
/// (NOTE: these Tags are only used for "PUT DO", but GETting
|
||||||
/// timestamp information from the card uses the combined Tag CD)
|
/// timestamp information from the card uses the combined Tag CD)
|
||||||
fn timestamp_put_tag(&self) -> u8 {
|
fn timestamp_put_tag(&self) -> Tag {
|
||||||
match self {
|
match self {
|
||||||
Self::Signing => 0xCE,
|
Self::Signing => Tags::GenerationTimeSignature,
|
||||||
Self::Decryption => 0xCF,
|
Self::Decryption => Tags::GenerationTimeDecryption,
|
||||||
Self::Authentication => 0xD0,
|
Self::Authentication => Tags::GenerationTimeAuthentication,
|
||||||
Self::Attestation => 0xDD,
|
Self::Attestation => Tags::GenerationTimeAttestation,
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial}
|
||||||
use crate::tlv::{value::Value, Tlv};
|
use crate::tlv::{value::Value, Tlv};
|
||||||
use crate::{
|
use crate::{
|
||||||
apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes,
|
apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes,
|
||||||
|
Tag, Tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An OpenPGP card access object, backed by a CardBackend implementation.
|
/// An OpenPGP card access object, backed by a CardBackend implementation.
|
||||||
|
@ -116,7 +117,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
|
||||||
let tlv = Tlv::try_from(resp.data()?)?;
|
let tlv = Tlv::try_from(resp.data()?)?;
|
||||||
let res = tlv.find(&[0x93].into()).ok_or_else(|| {
|
let res = tlv.find(Tag::from([0x93])).ok_or_else(|| {
|
||||||
Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string())
|
Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -192,8 +193,8 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
log::info!("OpenPgpTransaction: select_data");
|
log::info!("OpenPgpTransaction: select_data");
|
||||||
|
|
||||||
let tlv = Tlv::new(
|
let tlv = Tlv::new(
|
||||||
[0x60],
|
Tags::GeneralReference,
|
||||||
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
|
Value::C(vec![Tlv::new(Tags::TagList, Value::S(tag.to_vec()))]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = tlv.serialize();
|
let data = tlv.serialize();
|
||||||
|
@ -487,13 +488,13 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
// -> 86 xx External Public Key
|
// -> 86 xx External Public Key
|
||||||
|
|
||||||
// External Public Key
|
// External Public Key
|
||||||
let epk = Tlv::new([0x86], Value::S(eph.to_vec()));
|
let epk = Tlv::new(Tags::ExternalPublicKey, Value::S(eph.to_vec()));
|
||||||
|
|
||||||
// Public Key DO
|
// Public Key DO
|
||||||
let pkdo = Tlv::new([0x7f, 0x49], Value::C(vec![epk]));
|
let pkdo = Tlv::new(Tags::PublicKey, Value::C(vec![epk]));
|
||||||
|
|
||||||
// Cipher DO
|
// Cipher DO
|
||||||
let cdo = Tlv::new([0xa6], Value::C(vec![pkdo]));
|
let cdo = Tlv::new(Tags::Cipher, Value::C(vec![pkdo]));
|
||||||
|
|
||||||
self.pso_decipher(cdo.serialize())
|
self.pso_decipher(cdo.serialize())
|
||||||
}
|
}
|
||||||
|
@ -652,7 +653,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
log::info!("OpenPgpTransaction: set_algorithm_attributes");
|
log::info!("OpenPgpTransaction: set_algorithm_attributes");
|
||||||
|
|
||||||
// Command to PUT the algorithm attributes
|
// Command to PUT the algorithm attributes
|
||||||
let cmd = commands::put_data(&[key_type.algorithm_tag()], algo.to_data_object()?);
|
let cmd = commands::put_data(key_type.algorithm_tag(), algo.to_data_object()?);
|
||||||
|
|
||||||
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -684,7 +685,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_fingerprint(&mut self, fp: Fingerprint, key_type: KeyType) -> Result<(), Error> {
|
pub fn set_fingerprint(&mut self, fp: Fingerprint, key_type: KeyType) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_fingerprint");
|
log::info!("OpenPgpTransaction: set_fingerprint");
|
||||||
|
|
||||||
let fp_cmd = commands::put_data(&[key_type.fingerprint_put_tag()], fp.as_bytes().to_vec());
|
let fp_cmd = commands::put_data(key_type.fingerprint_put_tag(), fp.as_bytes().to_vec());
|
||||||
|
|
||||||
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -692,21 +693,21 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_ca_fingerprint_1(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
pub fn set_ca_fingerprint_1(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_ca_fingerprint_1");
|
log::info!("OpenPgpTransaction: set_ca_fingerprint_1");
|
||||||
|
|
||||||
let fp_cmd = commands::put_data(&[0xCA], fp.as_bytes().to_vec());
|
let fp_cmd = commands::put_data(Tags::CaFingerprint1, fp.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ca_fingerprint_2(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
pub fn set_ca_fingerprint_2(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_ca_fingerprint_2");
|
log::info!("OpenPgpTransaction: set_ca_fingerprint_2");
|
||||||
|
|
||||||
let fp_cmd = commands::put_data(&[0xCB], fp.as_bytes().to_vec());
|
let fp_cmd = commands::put_data(Tags::CaFingerprint2, fp.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ca_fingerprint_3(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
pub fn set_ca_fingerprint_3(&mut self, fp: Fingerprint) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_ca_fingerprint_3");
|
log::info!("OpenPgpTransaction: set_ca_fingerprint_3");
|
||||||
|
|
||||||
let fp_cmd = commands::put_data(&[0xCC], fp.as_bytes().to_vec());
|
let fp_cmd = commands::put_data(Tags::CaFingerprint3, fp.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
apdu::send_command(self.tx(), fp_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,7 +727,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
.copied()
|
.copied()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let time_cmd = commands::put_data(&[key_type.timestamp_put_tag()], time_value);
|
let time_cmd = commands::put_data(key_type.timestamp_put_tag(), time_value);
|
||||||
|
|
||||||
apdu::send_command(self.tx(), time_cmd, false)?.try_into()
|
apdu::send_command(self.tx(), time_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -740,7 +741,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_resetting_code(&mut self, resetting_code: &[u8]) -> Result<(), Error> {
|
pub fn set_resetting_code(&mut self, resetting_code: &[u8]) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_resetting_code");
|
log::info!("OpenPgpTransaction: set_resetting_code");
|
||||||
|
|
||||||
let cmd = commands::put_data(&[0xd3], resetting_code.to_vec());
|
let cmd = commands::put_data(Tags::ResettingCode, resetting_code.to_vec());
|
||||||
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +751,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> {
|
pub fn set_uif_pso_cds(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_uif_pso_cds");
|
log::info!("OpenPgpTransaction: set_uif_pso_cds");
|
||||||
|
|
||||||
let cmd = commands::put_data(&[0xd6], uif.as_bytes().to_vec());
|
let cmd = commands::put_data(Tags::UifSig, uif.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,7 +759,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> {
|
pub fn set_uif_pso_dec(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_uif_pso_dec");
|
log::info!("OpenPgpTransaction: set_uif_pso_dec");
|
||||||
|
|
||||||
let cmd = commands::put_data(&[0xd7], uif.as_bytes().to_vec());
|
let cmd = commands::put_data(Tags::UifDec, uif.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,7 +767,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> {
|
pub fn set_uif_pso_aut(&mut self, uif: &UIF) -> Result<(), Error> {
|
||||||
log::info!("OpenPgpTransaction: set_uif_pso_aut");
|
log::info!("OpenPgpTransaction: set_uif_pso_aut");
|
||||||
|
|
||||||
let cmd = commands::put_data(&[0xd8], uif.as_bytes().to_vec());
|
let cmd = commands::put_data(Tags::UifAuth, uif.as_bytes().to_vec());
|
||||||
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
apdu::send_command(self.tx(), cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,20 +871,20 @@ fn digestinfo(hash: Hash) -> Vec<u8> {
|
||||||
match hash {
|
match hash {
|
||||||
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
|
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
|
||||||
let tlv = Tlv::new(
|
let tlv = Tlv::new(
|
||||||
[0x30],
|
Tags::Sequence,
|
||||||
Value::C(vec![
|
Value::C(vec![
|
||||||
Tlv::new(
|
Tlv::new(
|
||||||
[0x30],
|
Tags::Sequence,
|
||||||
Value::C(vec![
|
Value::C(vec![
|
||||||
Tlv::new(
|
Tlv::new(
|
||||||
[0x06],
|
Tags::ObjectIdentifier,
|
||||||
// unwrapping is ok, for SHA*
|
// unwrapping is ok, for SHA*
|
||||||
Value::S(hash.oid().unwrap().to_vec()),
|
Value::S(hash.oid().unwrap().to_vec()),
|
||||||
),
|
),
|
||||||
Tlv::new([0x05], Value::S(vec![])),
|
Tlv::new(Tags::Null, Value::S(vec![])),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
Tlv::new([0x04], Value::S(hash.digest().to_vec())),
|
Tlv::new(Tags::OctetString, Value::S(hash.digest().to_vec())),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
pub(crate) mod length;
|
pub(crate) mod length;
|
||||||
|
@ -28,13 +28,14 @@ impl Tlv {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the first occurrence of `tag` and return its value (if any)
|
/// Find the first occurrence of `tag` and return its value (if any)
|
||||||
pub fn find(&self, tag: &Tag) -> Option<&Value> {
|
pub fn find<T: Clone + Into<Tag>>(&self, tag: T) -> Option<&Value> {
|
||||||
if &self.tag == tag {
|
let t: Tag = tag.clone().into();
|
||||||
|
if self.tag == t {
|
||||||
Some(&self.value)
|
Some(&self.value)
|
||||||
} else {
|
} else {
|
||||||
if let Value::C(inner) = &self.value {
|
if let Value::C(inner) = &self.value {
|
||||||
for tlv in inner {
|
for tlv in inner {
|
||||||
if let Some(found) = tlv.find(tag) {
|
if let Some(found) = tlv.find(tag.clone()) {
|
||||||
return Some(found);
|
return Some(found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,12 +85,12 @@ mod test {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use super::{Tlv, Value};
|
use super::{Tlv, Value};
|
||||||
use crate::Error;
|
use crate::{Error, Tags};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tlv0() {
|
fn test_tlv0() {
|
||||||
let cpkt = Tlv::new(
|
let cpkt = Tlv::new(
|
||||||
[0x7F, 0x48],
|
Tags::CardholderPrivateKeyTemplate,
|
||||||
Value::S(vec![
|
Value::S(vec![
|
||||||
0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00,
|
0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00,
|
||||||
]),
|
]),
|
||||||
|
@ -109,16 +110,22 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tlv,
|
tlv,
|
||||||
Tlv::new([0x5b], Value::S(hex!("546573743C3C5465737469").to_vec()))
|
Tlv::new(
|
||||||
|
Tags::Name,
|
||||||
|
Value::S(hex!("546573743C3C5465737469").to_vec())
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let (input, tlv) = Tlv::parse(input).unwrap();
|
let (input, tlv) = Tlv::parse(input).unwrap();
|
||||||
|
|
||||||
assert_eq!(tlv, Tlv::new([0x5f, 0x2d], Value::S(hex!("6465").to_vec())));
|
assert_eq!(
|
||||||
|
tlv,
|
||||||
|
Tlv::new(Tags::LanguagePref, Value::S(hex!("6465").to_vec()))
|
||||||
|
);
|
||||||
|
|
||||||
let (input, tlv) = Tlv::parse(input).unwrap();
|
let (input, tlv) = Tlv::parse(input).unwrap();
|
||||||
|
|
||||||
assert_eq!(tlv, Tlv::new([0x5f, 0x35], Value::S(hex!("31").to_vec())));
|
assert_eq!(tlv, Tlv::new(Tags::Sex, Value::S(hex!("31").to_vec())));
|
||||||
|
|
||||||
assert!(input.is_empty());
|
assert!(input.is_empty());
|
||||||
|
|
||||||
|
@ -136,69 +143,69 @@ mod test {
|
||||||
assert_eq!(serialized, data.to_vec());
|
assert_eq!(serialized, data.to_vec());
|
||||||
|
|
||||||
// outermost layer contains all bytes as value
|
// outermost layer contains all bytes as value
|
||||||
let value = tlv.find(&[0x6e].into()).unwrap();
|
let value = tlv.find(Tags::ApplicationRelatedData).unwrap();
|
||||||
assert_eq!(value.serialize(),
|
assert_eq!(value.serialize(),
|
||||||
hex!("4f10d27600012401030400061601918000005f520800730000e00590007f740381012073820110c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
|
hex!("4f10d27600012401030400061601918000005f520800730000e00590007f740381012073820110c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
|
||||||
|
|
||||||
// get and verify data for ecap tag
|
// get and verify data for ecap tag
|
||||||
let value = tlv.find(&[0xc0].into()).unwrap();
|
let value = tlv.find(Tags::ExtendedCapabilities).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
|
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0x4f].into()).unwrap();
|
let value = tlv.find(Tags::ApplicationIdentifier).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("d2760001240103040006160191800000"));
|
assert_eq!(value.serialize(), hex!("d2760001240103040006160191800000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0x5f, 0x52].into()).unwrap();
|
let value = tlv.find(Tags::HistoricalBytes).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("00730000e0059000"));
|
assert_eq!(value.serialize(), hex!("00730000e0059000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0x7f, 0x74].into()).unwrap();
|
let value = tlv.find(Tags::GeneralFeatureManagement).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("810120"));
|
assert_eq!(value.serialize(), hex!("810120"));
|
||||||
|
|
||||||
let value = tlv.find(&[0x73].into()).unwrap();
|
let value = tlv.find(Tags::DiscretionaryDataObjects).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
|
assert_eq!(value.serialize(), hex!("c00a7d000bfe080000ff0000c106010800001100c206010800001100c306010800001100da06010800001100c407ff7f7f7f030003c5500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd1000000000000000000000000000000000de0801000200030081027f660802020bfe02020bfed6020020d7020020d8020020d9020020"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc0].into()).unwrap();
|
let value = tlv.find(Tags::ExtendedCapabilities).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
|
assert_eq!(value.serialize(), hex!("7d000bfe080000ff0000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc1].into()).unwrap();
|
let value = tlv.find(Tags::AlgorithmAttributesSignature).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("010800001100"));
|
assert_eq!(value.serialize(), hex!("010800001100"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc2].into()).unwrap();
|
let value = tlv.find(Tags::AlgorithmAttributesDecryption).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("010800001100"));
|
assert_eq!(value.serialize(), hex!("010800001100"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc3].into()).unwrap();
|
let value = tlv.find(Tags::AlgorithmAttributesAuthentication).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("010800001100"));
|
assert_eq!(value.serialize(), hex!("010800001100"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xda].into()).unwrap();
|
let value = tlv.find(Tags::AlgorithmAttributesAttestation).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("010800001100"));
|
assert_eq!(value.serialize(), hex!("010800001100"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc4].into()).unwrap();
|
let value = tlv.find(Tags::PWStatusBytes).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("ff7f7f7f030003"));
|
assert_eq!(value.serialize(), hex!("ff7f7f7f030003"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc5].into()).unwrap();
|
let value = tlv.find(Tags::Fingerprints).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xc6].into()).unwrap();
|
let value = tlv.find(Tags::CaFingerprints).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
assert_eq!(value.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xcd].into()).unwrap();
|
let value = tlv.find(Tags::GenerationTimes).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("00000000000000000000000000000000"));
|
assert_eq!(value.serialize(), hex!("00000000000000000000000000000000"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xde].into()).unwrap();
|
let value = tlv.find(Tags::KeyInformation).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0100020003008102"));
|
assert_eq!(value.serialize(), hex!("0100020003008102"));
|
||||||
|
|
||||||
let value = tlv.find(&[0x7f, 0x66].into()).unwrap();
|
let value = tlv.find(Tags::ExtendedLengthInformation).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("02020bfe02020bfe"));
|
assert_eq!(value.serialize(), hex!("02020bfe02020bfe"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xd6].into()).unwrap();
|
let value = tlv.find(Tags::UifSig).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0020"));
|
assert_eq!(value.serialize(), hex!("0020"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xd7].into()).unwrap();
|
let value = tlv.find(Tags::UifDec).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0020"));
|
assert_eq!(value.serialize(), hex!("0020"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xd8].into()).unwrap();
|
let value = tlv.find(Tags::UifAuth).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0020"));
|
assert_eq!(value.serialize(), hex!("0020"));
|
||||||
|
|
||||||
let value = tlv.find(&[0xd9].into()).unwrap();
|
let value = tlv.find(Tags::UifAttestation).unwrap();
|
||||||
assert_eq!(value.serialize(), hex!("0020"));
|
assert_eq!(value.serialize(), hex!("0020"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -210,11 +217,14 @@ mod test {
|
||||||
// but has been abridged and changed. It does not represent a
|
// but has been abridged and changed. It does not represent a
|
||||||
// complete valid OpenPGP card DO!
|
// complete valid OpenPGP card DO!
|
||||||
|
|
||||||
let a = Tlv::new([0x7F, 0x48], Value::S(vec![0x92, 0x03]));
|
let a = Tlv::new(
|
||||||
|
Tags::CardholderPrivateKeyTemplate,
|
||||||
|
Value::S(vec![0x92, 0x03]),
|
||||||
|
);
|
||||||
|
|
||||||
let b = Tlv::new([0x5F, 0x48], Value::S(vec![0x1, 0x2, 0x3]));
|
let b = Tlv::new(Tags::ConcatenatedKeyData, Value::S(vec![0x1, 0x2, 0x3]));
|
||||||
|
|
||||||
let tlv = Tlv::new([0x4d], Value::C(vec![a, b]));
|
let tlv = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![a, b]));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tlv.serialize(),
|
tlv.serialize(),
|
||||||
|
|
Loading…
Reference in a new issue