use std::convert::{TryFrom, TryInto};
use std::fmt::{Display, Formatter, Write};
use std::time::{Duration, UNIX_EPOCH};
use chrono::{DateTime, Utc};
use crate::tags::Tags;
use crate::{algorithm::AlgorithmAttributes, tlv::Tlv, Error, KeyType};
mod algo_attrs;
mod algo_info;
mod application_id;
mod cardholder;
mod extended_cap;
mod extended_length_info;
mod fingerprint;
mod historical;
mod kdf_do;
mod key_generation_times;
mod pw_status;
pub struct ApplicationRelatedData(pub(crate) Tlv);
impl ApplicationRelatedData {
pub fn application_id(&self) -> Result<ApplicationIdentifier, Error> {
let aid = self.0.find(Tags::ApplicationIdentifier);
if let Some(aid) = aid {
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
} else {
Err(Error::NotFound("Couldn't get Application ID.".to_string()))
}
}
pub fn historical_bytes(&self) -> Result<HistoricalBytes, Error> {
let hist = self.0.find(Tags::HistoricalBytes);
if let Some(hist) = hist {
log::trace!("Historical bytes: {:x?}", hist);
(hist.serialize().as_slice()).try_into()
} else {
Err(Error::NotFound(
"Failed to get historical bytes.".to_string(),
))
}
}
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
let eli = self.0.find(Tags::ExtendedLengthInformation);
log::trace!("Extended length information: {:x?}", eli);
if let Some(eli) = eli {
Ok(Some((&eli.serialize()[..]).try_into()?))
} else {
Ok(None)
}
}
#[allow(dead_code)]
fn general_feature_management() -> Option<bool> {
unimplemented!()
}
#[allow(dead_code)]
fn discretionary_data_objects() {
unimplemented!()
}
pub fn extended_capabilities(&self) -> Result<ExtendedCapabilities, Error> {
let app_id = self.application_id()?;
let version = app_id.version();
let ecap = self.0.find(Tags::ExtendedCapabilities);
if let Some(ecap) = ecap {
Ok(ExtendedCapabilities::try_from((
&ecap.serialize()[..],
version,
))?)
} else {
Err(Error::NotFound(
"Failed to get extended capabilities.".to_string(),
))
}
}
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<AlgorithmAttributes, Error> {
let aa = self.0.find(key_type.algorithm_tag());
if let Some(aa) = aa {
AlgorithmAttributes::try_from(&aa.serialize()[..])
} else {
Err(Error::NotFound(format!(
"Failed to get algorithm attributes for {key_type:?}."
)))
}
}
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
let psb = self.0.find(Tags::PWStatusBytes);
if let Some(psb) = psb {
let pws = (&psb.serialize()[..]).try_into()?;
log::trace!("PW Status: {:x?}", pws);
Ok(pws)
} else {
Err(Error::NotFound(
"Failed to get PW status Bytes.".to_string(),
))
}
}
pub fn fingerprints(&self) -> Result<KeySet<Fingerprint>, Error> {
let fp = self.0.find(Tags::Fingerprints);
if let Some(fp) = fp {
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
log::trace!("Fp: {:x?}", fp);
Ok(fp)
} else {
Err(Error::NotFound("Failed to get fingerprints.".into()))
}
}
pub fn ca_fingerprints(&self) -> Result<[Option<Fingerprint>; 3], Error> {
let fp = self.0.find(Tags::CaFingerprints);
if let Some(fp) = fp {
let fp: KeySet<Fingerprint> = (&fp.serialize()[..]).try_into()?;
let fp = [fp.signature, fp.decryption, fp.authentication];
log::trace!("CA Fp: {:x?}", fp);
Ok(fp)
} else {
Err(Error::NotFound("Failed to get CA fingerprints.".into()))
}
}
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, Error> {
let kg = self.0.find(Tags::GenerationTimes);
if let Some(kg) = kg {
let kg: KeySet<KeyGenerationTime> = (&kg.serialize()[..]).try_into()?;
log::trace!("Key generation: {:x?}", kg);
Ok(kg)
} else {
Err(Error::NotFound(
"Failed to get key generation times.".to_string(),
))
}
}
pub fn key_information(&self) -> Result<Option<KeyInformation>, Error> {
let ki = self.0.find(Tags::KeyInformation);
Ok(ki.map(|v| v.serialize().into()))
}
pub fn uif_pso_cds(&self) -> Result<Option<UserInteractionFlag>, Error> {
let uif = self.0.find(Tags::UifSig);
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
pub fn uif_pso_dec(&self) -> Result<Option<UserInteractionFlag>, Error> {
let uif = self.0.find(Tags::UifDec);
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
pub fn uif_pso_aut(&self) -> Result<Option<UserInteractionFlag>, Error> {
let uif = self.0.find(Tags::UifAuth);
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
pub fn attestation_key_fingerprint(&self) -> Result<Option<Fingerprint>, Error> {
match self.0.find(Tags::FingerprintAttestation) {
None => Ok(None),
Some(data) => {
if data.serialize().iter().any(|&b| b != 0) {
Ok(Some(Fingerprint::try_from(data.serialize().as_slice())?))
} else {
Ok(None)
}
}
}
}
pub fn attestation_key_algorithm_attributes(
&mut self,
) -> Result<Option<AlgorithmAttributes>, Error> {
match self.0.find(Tags::AlgorithmAttributesAttestation) {
None => Ok(None),
Some(data) => Ok(Some(AlgorithmAttributes::try_from(
data.serialize().as_slice(),
)?)),
}
}
pub fn attestation_key_generation_time(&self) -> Result<Option<KeyGenerationTime>, Error> {
match self.0.find(Tags::GenerationTimeAttestation) {
None => Ok(None),
Some(data) => {
assert_eq!(data.serialize().len(), 4);
match u32::from_be_bytes(data.serialize().try_into().unwrap()) {
0 => Ok(None),
kgt => Ok(Some(kgt.into())),
}
}
}
}
pub fn uif_attestation(&self) -> Result<Option<UserInteractionFlag>, Error> {
let uif = self.0.find(Tags::UifAttestation);
match uif {
None => Ok(None),
Some(v) => Ok(Some(v.serialize().try_into()?)),
}
}
}
#[derive(Debug)]
pub struct SecuritySupportTemplate {
pub(crate) dsc: u32,
}
impl SecuritySupportTemplate {
pub fn signature_count(&self) -> u32 {
self.dsc
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct KeyGenerationTime(u32);
impl KeyGenerationTime {
pub fn get(&self) -> u32 {
self.0
}
pub fn to_datetime(&self) -> DateTime<Utc> {
let d = UNIX_EPOCH + Duration::from_secs(self.get() as u64);
DateTime::<Utc>::from(d)
}
}
impl Display for KeyGenerationTime {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_datetime())
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct UserInteractionFlag([u8; 2]);
impl TryFrom<Vec<u8>> for UserInteractionFlag {
type Error = Error;
fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
if v.len() == 2 {
Ok(UserInteractionFlag(v.try_into().unwrap()))
} else {
Err(Error::ParseError(format!("Can't get UID from {v:x?}")))
}
}
}
impl UserInteractionFlag {
pub fn touch_policy(&self) -> TouchPolicy {
self.0[0].into()
}
pub fn set_touch_policy(&mut self, tm: TouchPolicy) {
self.0[0] = tm.into();
}
pub fn features(&self) -> Features {
self.0[1].into()
}
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.0[..]
}
}
impl Display for UserInteractionFlag {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Touch policy: {} [Features: {}]",
self.touch_policy(),
self.features()
)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[non_exhaustive]
pub enum TouchPolicy {
Off,
On,
Fixed,
Cached,
CachedFixed,
Unknown(u8),
}
impl TouchPolicy {
pub fn touch_required(&self) -> bool {
!matches!(self, Self::Off)
}
}
impl Display for TouchPolicy {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TouchPolicy::Off => write!(f, "Off"),
TouchPolicy::On => write!(f, "On"),
TouchPolicy::Fixed => write!(f, "Fixed"),
TouchPolicy::Cached => write!(f, "Cached"),
TouchPolicy::CachedFixed => write!(f, "CachedFixed"),
TouchPolicy::Unknown(i) => write!(f, "Unknown({i})"),
}
}
}
impl From<TouchPolicy> for u8 {
fn from(tm: TouchPolicy) -> Self {
match tm {
TouchPolicy::Off => 0,
TouchPolicy::On => 1,
TouchPolicy::Fixed => 2,
TouchPolicy::Cached => 3,
TouchPolicy::CachedFixed => 4,
TouchPolicy::Unknown(i) => i,
}
}
}
impl From<u8> for TouchPolicy {
fn from(i: u8) -> Self {
match i {
0 => TouchPolicy::Off,
1 => TouchPolicy::On,
2 => TouchPolicy::Fixed,
3 => TouchPolicy::Cached,
4 => TouchPolicy::CachedFixed,
_ => TouchPolicy::Unknown(i),
}
}
}
pub struct Features(u8);
impl From<u8> for Features {
fn from(i: u8) -> Self {
Features(i)
}
}
impl Display for Features {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut ft = vec![];
if self.0 & 0x80 != 0 {
ft.push("Display")
}
if self.0 & 0x40 != 0 {
ft.push("Biometric input sensor")
}
if self.0 & 0x20 != 0 {
ft.push("Button")
}
if self.0 & 0x10 != 0 {
ft.push("Keypad")
}
if self.0 & 0x8 != 0 {
ft.push("LED")
}
if self.0 & 0x4 != 0 {
ft.push("Loudspeaker")
}
if self.0 & 0x2 != 0 {
ft.push("Microphone")
}
if self.0 & 0x1 != 0 {
ft.push("Touchscreen")
}
write!(f, "{}", ft.join(", "))
}
}
pub struct KeyInformation(Vec<u8>);
impl From<Vec<u8>> for KeyInformation {
fn from(v: Vec<u8>) -> Self {
KeyInformation(v)
}
}
impl KeyInformation {
pub fn num_additional(&self) -> usize {
(self.0.len() - 6) / 2
}
fn get_ref(&self, n: usize) -> u8 {
self.0[n * 2]
}
fn get_status(&self, n: usize) -> KeyStatus {
self.0[n * 2 + 1].into()
}
pub fn sig_ref(&self) -> u8 {
self.get_ref(0)
}
pub fn sig_status(&self) -> KeyStatus {
self.get_status(0)
}
pub fn dec_ref(&self) -> u8 {
self.get_ref(1)
}
pub fn dec_status(&self) -> KeyStatus {
self.get_status(1)
}
pub fn aut_ref(&self) -> u8 {
self.get_ref(2)
}
pub fn aut_status(&self) -> KeyStatus {
self.get_status(2)
}
pub fn additional_ref(&self, num: usize) -> u8 {
self.get_ref(3 + num)
}
pub fn additional_status(&self, num: usize) -> KeyStatus {
self.get_status(3 + num)
}
}
impl Display for KeyInformation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"signature key (#{}): {}",
self.sig_ref(),
self.sig_status()
)?;
writeln!(
f,
"decryption key (#{}): {}",
self.dec_ref(),
self.dec_status()
)?;
writeln!(
f,
"authentication key (#{}): {}",
self.aut_ref(),
self.aut_status()
)?;
for i in 0..self.num_additional() {
writeln!(
f,
"additional key {} (#{}): {}",
i,
self.additional_ref(i),
self.additional_status(i)
)?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum KeyStatus {
NotPresent,
Generated,
Imported,
Unknown(u8),
}
impl From<u8> for KeyStatus {
fn from(i: u8) -> Self {
match i {
0 => KeyStatus::NotPresent,
1 => KeyStatus::Generated,
2 => KeyStatus::Imported,
_ => KeyStatus::Unknown(i),
}
}
}
impl Display for KeyStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
KeyStatus::NotPresent => write!(f, "not present"),
KeyStatus::Generated => write!(f, "generated"),
KeyStatus::Imported => write!(f, "imported"),
KeyStatus::Unknown(i) => write!(f, "unknown status ({i})"),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ApplicationIdentifier {
application: u8,
version: u16,
manufacturer: u16,
serial: u32,
}
impl Display for ApplicationIdentifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"D276000124 {:02X} {:04X} {:04X} {:08X} 0000",
self.application, self.version, self.manufacturer, self.serial
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HistoricalBytes {
cib: u8,
csd: Option<CardServiceData>,
cc: Option<CardCapabilities>,
sib: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CardCapabilities {
command_chaining: bool,
extended_lc_le: bool,
extended_length_information: bool,
}
impl Display for CardCapabilities {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.command_chaining {
writeln!(f, "- command chaining")?;
}
if self.extended_lc_le {
writeln!(f, "- extended Lc and Le fields")?;
}
if self.extended_length_information {
writeln!(f, "- extended Length Information")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CardServiceData {
select_by_full_df_name: bool, select_by_partial_df_name: bool, dos_available_in_ef_dir: bool,
dos_available_in_ef_atr_info: bool, access_services: [bool; 3], mf: bool,
}
impl Display for CardServiceData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.select_by_full_df_name {
writeln!(f, "- Application Selection by full DF name")?;
}
if self.select_by_partial_df_name {
writeln!(f, "- Application Selection by partial DF name")?;
}
if self.dos_available_in_ef_dir {
writeln!(f, "- DOs available in EF.DIR")?;
}
if self.dos_available_in_ef_atr_info {
writeln!(f, "- DOs available in EF.ATR/INFO")?;
}
write!(
f,
"- EF.DIR and EF.ATR/INFO access services by the GET DATA command (BER-TLV): "
)?;
for a in self.access_services {
if a {
write!(f, "1")?;
} else {
write!(f, "0")?;
}
}
writeln!(f)?;
if self.mf {
writeln!(f, "- Card with MF")?;
}
Ok(())
}
}
#[derive(Debug, Eq, Clone, Copy, PartialEq)]
pub struct ExtendedCapabilities {
secure_messaging: bool,
get_challenge: bool,
key_import: bool,
pw_status_change: bool,
private_use_dos: bool,
algo_attrs_changeable: bool,
aes: bool,
kdf_do: bool,
sm_algo: u8,
max_len_challenge: u16,
max_len_cardholder_cert: u16,
max_cmd_len: Option<u16>, max_resp_len: Option<u16>, max_len_special_do: Option<u16>, pin_block_2_format_support: Option<bool>, mse_command_support: Option<bool>, }
impl Display for ExtendedCapabilities {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if self.secure_messaging {
writeln!(f, "- secure messaging")?;
}
if self.get_challenge {
writeln!(f, "- get challenge")?;
}
if self.key_import {
writeln!(f, "- key import")?;
}
if self.pw_status_change {
writeln!(f, "- PW Status changeable")?;
}
if self.private_use_dos {
writeln!(f, "- private use DOs")?;
}
if self.algo_attrs_changeable {
writeln!(f, "- algorithm attributes changeable")?;
}
if self.aes {
writeln!(f, "- PSO:DEC/ENC with AES")?;
}
if self.kdf_do {
writeln!(f, "- KDF-DO")?;
}
if self.sm_algo != 0 {
writeln!(f, "- secure messaging algorithm: {:#02X}", self.sm_algo)?;
}
if self.max_len_challenge != 0 {
writeln!(
f,
"- maximum length of challenge: {}",
self.max_len_challenge
)?;
}
writeln!(
f,
"- maximum length cardholder certificates: {}",
self.max_len_cardholder_cert
)?;
if let Some(max_cmd_len) = self.max_cmd_len {
writeln!(f, "- maximum command length: {max_cmd_len}")?;
}
if let Some(max_resp_len) = self.max_resp_len {
writeln!(f, "- maximum response length: {max_resp_len}")?;
}
if let Some(max_len_special_do) = self.max_len_special_do {
writeln!(f, "- maximum length for special DOs: {max_len_special_do}")?;
}
if self.pin_block_2_format_support == Some(true) {
writeln!(f, "- PIN block 2 format supported")?;
}
if self.mse_command_support == Some(true) {
writeln!(f, "- MSE command (for DEC and AUT) supported")?;
}
Ok(())
}
}
#[derive(Debug, Eq, Clone, Copy, PartialEq)]
pub struct ExtendedLengthInfo {
max_command_bytes: u16,
max_response_bytes: u16,
}
impl Display for ExtendedLengthInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "- max command length: {}", self.max_command_bytes)?;
writeln!(f, "- max response length: {}", self.max_response_bytes)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CardholderRelatedData {
name: Option<Vec<u8>>,
lang: Option<Vec<Lang>>,
sex: Option<Sex>,
}
impl Display for CardholderRelatedData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(name) = &self.name {
writeln!(f, "Name: {}", Self::latin1_to_string(name))?;
}
if let Some(sex) = self.sex {
writeln!(f, "Sex: {sex}")?;
}
if let Some(lang) = &self.lang {
for (n, l) in lang.iter().enumerate() {
writeln!(f, "Lang {}: {}", n + 1, l)?;
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Sex {
NotKnown,
Male,
Female,
NotApplicable,
UndefinedValue(u8), }
impl Display for Sex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotKnown => write!(f, "Not known"),
Self::Male => write!(f, "Male"),
Self::Female => write!(f, "Female"),
Self::NotApplicable => write!(f, "Not applicable"),
Self::UndefinedValue(v) => write!(f, "Undefined value {v:x?}"),
}
}
}
impl From<&Sex> for u8 {
fn from(sex: &Sex) -> u8 {
match sex {
Sex::NotKnown => 0x30,
Sex::Male => 0x31,
Sex::Female => 0x32,
Sex::NotApplicable => 0x39,
Sex::UndefinedValue(v) => *v,
}
}
}
impl From<u8> for Sex {
fn from(s: u8) -> Self {
match s {
0x30 => Self::NotKnown,
0x31 => Self::Male,
0x32 => Self::Female,
0x39 => Self::NotApplicable,
v => Self::UndefinedValue(v),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Lang {
Value([u8; 2]),
Invalid(u8),
}
impl Display for Lang {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Value(v) => {
write!(f, "{}{}", v[0] as char, v[1] as char)
}
Self::Invalid(v) => {
write!(f, "{v:x?}")
}
}
}
}
impl From<(char, char)> for Lang {
fn from(c: (char, char)) -> Self {
Lang::Value([c.0 as u8, c.1 as u8])
}
}
impl From<[char; 2]> for Lang {
fn from(c: [char; 2]) -> Self {
Lang::Value([c[0] as u8, c[1] as u8])
}
}
impl From<Lang> for Vec<u8> {
fn from(lang: Lang) -> Self {
match lang {
Lang::Value(v) => vec![v[0], v[1]],
Lang::Invalid(v) => vec![v],
}
}
}
impl From<&[u8; 1]> for Lang {
fn from(data: &[u8; 1]) -> Self {
Lang::Invalid(data[0])
}
}
impl From<&[u8; 2]> for Lang {
fn from(data: &[u8; 2]) -> Self {
Lang::Value([data[0], data[1]])
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct PWStatusBytes {
pub(crate) pw1_cds_valid_once: bool,
pub(crate) pw1_pin_block: bool,
pub(crate) pw1_len_format: u8,
pub(crate) rc_len: u8,
pub(crate) pw3_pin_block: bool,
pub(crate) pw3_len_format: u8,
pub(crate) err_count_pw1: u8,
pub(crate) err_count_rst: u8,
pub(crate) err_count_pw3: u8,
}
impl PWStatusBytes {
pub fn set_pw1_pin_block(&mut self, val: bool) {
self.pw1_pin_block = val;
}
pub fn set_pw3_pin_block(&mut self, val: bool) {
self.pw3_pin_block = val;
}
pub fn pw1_cds_valid_once(&self) -> bool {
self.pw1_cds_valid_once
}
pub fn set_pw1_cds_valid_once(&mut self, val: bool) {
self.pw1_cds_valid_once = val;
}
pub fn pw1_max_len(&self) -> u8 {
self.pw1_len_format & 0x7f
}
pub fn rc_max_len(&self) -> u8 {
self.rc_len
}
pub fn pw3_max_len(&self) -> u8 {
self.pw3_len_format & 0x7f
}
pub fn err_count_pw1(&self) -> u8 {
self.err_count_pw1
}
pub fn err_count_rc(&self) -> u8 {
self.err_count_rst
}
pub fn err_count_pw3(&self) -> u8 {
self.err_count_pw3
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Fingerprint([u8; 20]);
impl Fingerprint {
pub fn to_spaced_hex(&self) -> String {
let mut fp = String::new();
for i in 0..20 {
let _ = write!(&mut fp, "{:02X}", self.0[i]);
if i < 19 && (i % 2 == 1) {
fp.push(' ');
}
if i == 9 {
fp.push(' ');
}
}
fp
}
}
#[derive(Debug, Clone)]
pub struct KdfDo {
kdf_algo: u8,
hash_algo: Option<u8>,
iter_count: Option<u32>,
salt_pw1: Option<Vec<u8>>,
salt_rc: Option<Vec<u8>>,
salt_pw3: Option<Vec<u8>>,
initial_hash_pw1: Option<Vec<u8>>,
initial_hash_pw3: Option<Vec<u8>>,
}
pub(crate) fn complete<O>(result: nom::IResult<&[u8], O>) -> Result<O, Error> {
let (rem, output) = result.map_err(|_err| Error::ParseError("Parsing failed".to_string()))?;
if rem.is_empty() {
Ok(output)
} else {
Err(Error::ParseError(format!(
"Parsing incomplete, trailing data: {rem:x?}"
)))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeySet<T> {
signature: Option<T>,
decryption: Option<T>,
authentication: Option<T>,
}
impl<T> From<(Option<T>, Option<T>, Option<T>)> for KeySet<T> {
fn from(tuple: (Option<T>, Option<T>, Option<T>)) -> Self {
Self {
signature: tuple.0,
decryption: tuple.1,
authentication: tuple.2,
}
}
}
impl<T> KeySet<T> {
pub fn signature(&self) -> Option<&T> {
self.signature.as_ref()
}
pub fn decryption(&self) -> Option<&T> {
self.decryption.as_ref()
}
pub fn authentication(&self) -> Option<&T> {
self.authentication.as_ref()
}
}