output: add module that models output for various subcommands

Each subcommand has its own model, and models for each major version
of the output. This isn't used yet, but soon will be.

Sponsored-by: author
This commit is contained in:
Lars Wirzenius 2022-10-18 17:48:56 +03:00 committed by Lars Wirzenius
parent dd0b74c43b
commit eb0ad179f6
8 changed files with 947 additions and 0 deletions

View file

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct AttestationCert {
ident: String,
attestation_cert: String,
}
impl AttestationCert {
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn attestation_cert(&mut self, cert: String) {
self.attestation_cert = cert;
}
fn text(&self) -> Result<String, OpgpCardError> {
Ok(format!(
"OpenPGP card {}\n\n{}\n",
self.ident, self.attestation_cert,
))
}
fn v1(&self) -> Result<AttestationCertV0, OpgpCardError> {
Ok(AttestationCertV0 {
schema_version: AttestationCertV0::VERSION,
ident: self.ident.clone(),
attestation_cert: self.attestation_cert.clone(),
})
}
}
impl OutputBuilder for AttestationCert {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if AttestationCertV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if AttestationCertV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct AttestationCertV0 {
schema_version: OutputVersion,
ident: String,
attestation_cert: String,
}
impl OutputVariant for AttestationCertV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct AdminGenerate {
ident: String,
algorithm: String,
public_key: String,
}
impl AdminGenerate {
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn algorithm(&mut self, algorithm: String) {
self.algorithm = algorithm;
}
pub fn public_key(&mut self, key: String) {
self.public_key = key;
}
fn text(&self) -> Result<String, OpgpCardError> {
Ok(format!(
"OpenPGP card {}\n\n{}\n",
self.ident, self.public_key,
))
}
fn v1(&self) -> Result<AdminGenerateV0, OpgpCardError> {
Ok(AdminGenerateV0 {
schema_version: AdminGenerateV0::VERSION,
ident: self.ident.clone(),
algorithm: self.algorithm.clone(),
public_key: self.public_key.clone(),
})
}
}
impl OutputBuilder for AdminGenerate {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if AdminGenerateV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if AdminGenerateV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct AdminGenerateV0 {
schema_version: OutputVersion,
ident: String,
algorithm: String,
public_key: String,
}
impl OutputVariant for AdminGenerateV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,189 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct Info {
ident: String,
card_version: String,
application_id: String,
manufacturer_id: String,
manufacturer_name: String,
card_capabilities: Vec<String>,
card_service_data: String,
extended_length_info: Vec<String>,
extended_capabilities: Vec<String>,
algorithms: Option<Vec<String>>,
firmware_version: Option<String>,
}
impl Info {
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn card_version(&mut self, version: String) {
self.card_version = version;
}
pub fn application_id(&mut self, id: String) {
self.application_id = id;
}
pub fn manufacturer_id(&mut self, id: String) {
self.manufacturer_id = id;
}
pub fn manufacturer_name(&mut self, name: String) {
self.manufacturer_name = name;
}
pub fn card_capability(&mut self, capability: String) {
self.card_capabilities.push(capability);
}
pub fn card_service_data(&mut self, data: String) {
self.card_service_data = data;
}
pub fn extended_length_info(&mut self, info: String) {
self.extended_length_info.push(info);
}
pub fn extended_capability(&mut self, capability: String) {
self.extended_capabilities.push(capability);
}
pub fn algorithm(&mut self, algorithm: String) {
if let Some(ref mut algos) = self.algorithms {
algos.push(algorithm);
} else {
self.algorithms = Some(vec![algorithm]);
}
}
pub fn firmware_version(&mut self, version: String) {
self.firmware_version = Some(version);
}
fn text(&self) -> Result<String, OpgpCardError> {
let mut s = format!("OpenPGP card {}\n\n", self.ident);
s.push_str(&format!(
"Application Identifier: {}\n",
self.application_id
));
s.push_str(&format!(
"Manufacturer [{}]: {}\n\n",
self.manufacturer_id, self.manufacturer_name
));
if !self.card_capabilities.is_empty() {
s.push_str("Card Capabilities:\n");
for c in self.card_capabilities.iter() {
s.push_str(&format!("- {}\n", c));
}
s.push('\n');
}
if !self.card_service_data.is_empty() {
s.push_str(&format!("Card service data: {}\n", self.card_service_data));
s.push('\n');
}
if !self.extended_length_info.is_empty() {
s.push_str("Extended Length Info:\n");
for c in self.extended_length_info.iter() {
s.push_str(&format!("- {}\n", c));
}
s.push('\n');
}
s.push_str("Extended Capabilities:\n");
for c in self.extended_capabilities.iter() {
s.push_str(&format!("- {}\n", c));
}
s.push('\n');
if let Some(algos) = &self.algorithms {
s.push_str("Supported algorithms:\n");
for c in algos.iter() {
s.push_str(&format!("- {}\n", c));
}
s.push('\n');
}
if let Some(v) = &self.firmware_version {
s.push_str(&format!("Firmware Version: {}\n", v));
}
Ok(s)
}
fn v1(&self) -> Result<InfoV0, OpgpCardError> {
Ok(InfoV0 {
schema_version: InfoV0::VERSION,
ident: self.ident.clone(),
card_version: self.card_version.clone(),
application_id: self.application_id.clone(),
manufacturer_id: self.manufacturer_id.clone(),
manufacturer_name: self.manufacturer_name.clone(),
card_capabilities: self.card_capabilities.clone(),
card_service_data: self.card_service_data.clone(),
extended_length_info: self.extended_length_info.clone(),
extended_capabilities: self.extended_capabilities.clone(),
algorithms: self.algorithms.clone(),
firmware_version: self.firmware_version.clone(),
})
}
}
impl OutputBuilder for Info {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if InfoV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if InfoV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct InfoV0 {
schema_version: OutputVersion,
ident: String,
card_version: String,
application_id: String,
manufacturer_id: String,
manufacturer_name: String,
card_capabilities: Vec<String>,
card_service_data: String,
extended_length_info: Vec<String>,
extended_capabilities: Vec<String>,
algorithms: Option<Vec<String>>,
firmware_version: Option<String>,
}
impl OutputVariant for InfoV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Default, Debug, Serialize)]
pub struct List {
idents: Vec<String>,
}
impl List {
pub fn push(&mut self, idnet: String) {
self.idents.push(idnet);
}
fn text(&self) -> Result<String, OpgpCardError> {
let s = if self.idents.is_empty() {
"No OpenPGP cards found.".into()
} else {
let mut s = "Available OpenPGP cards:\n".to_string();
for id in self.idents.iter() {
s.push_str(&format!(" {}\n", id));
}
s
};
Ok(s)
}
fn v1(&self) -> Result<ListV0, OpgpCardError> {
Ok(ListV0 {
schema_version: ListV0::VERSION,
idents: self.idents.clone(),
})
}
}
impl OutputBuilder for List {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if ListV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if ListV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct ListV0 {
schema_version: OutputVersion,
idents: Vec<String>,
}
impl OutputVariant for ListV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::OutputVersion;
#[derive(Debug, thiserror::Error)]
pub enum OpgpCardError {
#[error("unknown output version {0}")]
UnknownVersion(OutputVersion),
#[error("failed to serialize JSON output with serde_json")]
SerdeJson(#[source] serde_json::Error),
#[error("failed to serialize YAML output with serde_yaml")]
SerdeYaml(#[source] serde_yaml::Error),
}
mod list;
pub use list::List;
mod status;
pub use status::{KeySlotInfo, Status};
mod info;
pub use info::Info;
mod ssh;
pub use ssh::Ssh;
mod pubkey;
pub use pubkey::PublicKey;
mod generate;
pub use generate::AdminGenerate;
mod attest;
pub use attest::AttestationCert;

View file

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct PublicKey {
ident: String,
public_key: String,
}
impl PublicKey {
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn public_key(&mut self, key: String) {
self.public_key = key;
}
fn text(&self) -> Result<String, OpgpCardError> {
Ok(format!(
"OpenPGP card {}\n\n{}\n",
self.ident, self.public_key
))
}
fn v1(&self) -> Result<PublicKeyV0, OpgpCardError> {
Ok(PublicKeyV0 {
schema_version: PublicKeyV0::VERSION,
ident: self.ident.clone(),
public_key: self.public_key.clone(),
})
}
}
impl OutputBuilder for PublicKey {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if PublicKeyV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if PublicKeyV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct PublicKeyV0 {
schema_version: OutputVersion,
ident: String,
public_key: String,
}
impl OutputVariant for PublicKeyV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct Ssh {
ident: String,
authentication_key_fingerprint: Option<String>,
ssh_public_key: Option<String>,
}
impl Ssh {
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn authentication_key_fingerprint(&mut self, fp: String) {
self.authentication_key_fingerprint = Some(fp);
}
pub fn ssh_public_key(&mut self, key: String) {
self.ssh_public_key = Some(key);
}
fn text(&self) -> Result<String, OpgpCardError> {
let mut s = format!("OpenPGP card {}\n", self.ident);
if let Some(fp) = &self.authentication_key_fingerprint {
s.push_str(&format!("Authentication key fingerprint:\n{}\n", fp));
}
if let Some(key) = &self.ssh_public_key {
s.push_str(&format!("SSH public key:\n{}\n", key));
}
Ok(s)
}
fn v1(&self) -> Result<SshV0, OpgpCardError> {
Ok(SshV0 {
schema_version: SshV0::VERSION,
ident: self.ident.clone(),
authentication_key_fingerprint: self.authentication_key_fingerprint.clone(),
ssh_public_key: self.ssh_public_key.clone(),
})
}
}
impl OutputBuilder for Ssh {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if SshV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if SshV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
struct SshV0 {
schema_version: OutputVersion,
ident: String,
authentication_key_fingerprint: Option<String>,
ssh_public_key: Option<String>,
}
impl OutputVariant for SshV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}

View file

@ -0,0 +1,327 @@
// SPDX-FileCopyrightText: 2022 Lars Wirzenius <liw@liw.fi>
// SPDX-License-Identifier: MIT OR Apache-2.0
use serde::Serialize;
use crate::output::OpgpCardError;
use crate::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
#[derive(Debug, Default, Serialize)]
pub struct Status {
verbose: bool,
ident: String,
card_version: String,
card_holder: Option<String>,
url: Option<String>,
language_preferences: Vec<String>,
signature_key: KeySlotInfo,
signature_count: u32,
decryption_key: KeySlotInfo,
authentication_key: KeySlotInfo,
user_pin_remaining_attempts: u8,
admin_pin_remaining_attempts: u8,
reset_code_remaining_attempts: u8,
card_touch_policy: String,
card_touch_features: String,
key_statuses: Vec<(u8, String)>,
ca_fingerprints: Vec<String>,
}
impl Status {
pub fn verbose(&mut self, verbose: bool) {
self.verbose = verbose;
}
pub fn ident(&mut self, ident: String) {
self.ident = ident;
}
pub fn card_version(&mut self, card_version: String) {
self.card_version = card_version;
}
pub fn card_holder(&mut self, card_holder: String) {
self.card_holder = Some(card_holder);
}
pub fn url(&mut self, url: String) {
self.url = Some(url);
}
pub fn language_preference(&mut self, pref: String) {
self.language_preferences.push(pref);
}
pub fn signature_key(&mut self, key: KeySlotInfo) {
self.signature_key = key;
}
pub fn signature_count(&mut self, count: u32) {
self.signature_count = count;
}
pub fn decryption_key(&mut self, key: KeySlotInfo) {
self.decryption_key = key;
}
pub fn authentication_key(&mut self, key: KeySlotInfo) {
self.authentication_key = key;
}
pub fn user_pin_remaining_attempts(&mut self, count: u8) {
self.user_pin_remaining_attempts = count;
}
pub fn admin_pin_remaining_attempts(&mut self, count: u8) {
self.admin_pin_remaining_attempts = count;
}
pub fn reset_code_remaining_attempts(&mut self, count: u8) {
self.reset_code_remaining_attempts = count;
}
pub fn card_touch_policy(&mut self, policy: String) {
self.card_touch_policy = policy;
}
pub fn card_touch_features(&mut self, features: String) {
self.card_touch_features = features;
}
pub fn key_status(&mut self, keyref: u8, status: String) {
self.key_statuses.push((keyref, status));
}
pub fn ca_fingerprint(&mut self, fingerprint: String) {
self.ca_fingerprints.push(fingerprint);
}
fn text(&self) -> Result<String, OpgpCardError> {
let mut s = String::new();
s.push_str(&format!(
"OpenPGP card {} (card version {})\n\n",
self.ident, self.card_version
));
let mut nl = false;
if let Some(name) = &self.card_holder {
if !name.is_empty() {
s.push_str(&format!("Cardholder: {}\n", name));
nl = true;
}
}
if let Some(url) = &self.url {
if !url.is_empty() {
s.push_str(&format!("URL: {}\n", url));
nl = true;
}
}
if !self.language_preferences.is_empty() {
let prefs = self.language_preferences.to_vec().join(", ");
if !prefs.is_empty() {
s.push_str(&format!("Language preferences: '{}'\n", prefs));
nl = true;
}
}
if nl {
s.push('\n');
}
s.push_str("Signature key:\n");
for line in self.signature_key.format(self.verbose) {
s.push_str(&format!(" {}\n", line));
}
s.push_str(&format!(" Signatures made: {}\n", self.signature_count));
s.push('\n');
s.push_str("Decryption key:\n");
for line in self.decryption_key.format(self.verbose) {
s.push_str(&format!(" {}\n", line));
}
s.push('\n');
s.push_str("Authentication key:\n");
for line in self.authentication_key.format(self.verbose) {
s.push_str(&format!(" {}\n", line));
}
s.push('\n');
s.push_str(&format!(
"Remaining PIN attempts: User: {}, Admin: {}, Reset Code: {}\n",
self.user_pin_remaining_attempts,
self.admin_pin_remaining_attempts,
self.reset_code_remaining_attempts
));
if self.verbose {
s.push_str(&format!(
"Touch policy attestation: {}\n",
self.card_touch_policy
));
for (keyref, status) in self.key_statuses.iter() {
s.push_str(&format!("Key status (#{}): {}\n", keyref, status));
}
}
Ok(s)
}
fn v1(&self) -> Result<StatusV0, OpgpCardError> {
Ok(StatusV0 {
schema_version: StatusV0::VERSION,
ident: self.ident.clone(),
card_version: self.card_version.clone(),
card_holder: self.card_holder.clone(),
url: self.url.clone(),
language_preferences: self.language_preferences.clone(),
signature_key: self.signature_key.clone(),
signature_count: self.signature_count,
decryption_key: self.decryption_key.clone(),
authentication_key: self.authentication_key.clone(),
user_pin_remaining_attempts: self.user_pin_remaining_attempts,
admin_pin_remaining_attempts: self.admin_pin_remaining_attempts,
reset_code_remaining_attempts: self.reset_code_remaining_attempts,
card_touch_policy: self.card_touch_policy.clone(),
card_touch_features: self.card_touch_features.clone(),
key_statuses: self.key_statuses.clone(),
ca_fingerprints: self.ca_fingerprints.clone(),
})
}
}
impl OutputBuilder for Status {
type Err = OpgpCardError;
fn print(&self, format: OutputFormat, version: OutputVersion) -> Result<String, Self::Err> {
match format {
OutputFormat::Json => {
let result = if StatusV0::VERSION.is_acceptable_for(&version) {
self.v1()?.json()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeJson)
}
OutputFormat::Yaml => {
let result = if StatusV0::VERSION.is_acceptable_for(&version) {
self.v1()?.yaml()
} else {
return Err(Self::Err::UnknownVersion(version));
};
result.map_err(Self::Err::SerdeYaml)
}
OutputFormat::Text => Ok(self.text()?),
}
}
}
#[derive(Debug, Serialize)]
pub struct StatusV0 {
schema_version: OutputVersion,
ident: String,
card_version: String,
card_holder: Option<String>,
url: Option<String>,
language_preferences: Vec<String>,
signature_key: KeySlotInfo,
signature_count: u32,
decryption_key: KeySlotInfo,
authentication_key: KeySlotInfo,
user_pin_remaining_attempts: u8,
admin_pin_remaining_attempts: u8,
reset_code_remaining_attempts: u8,
card_touch_policy: String,
card_touch_features: String,
key_statuses: Vec<(u8, String)>,
ca_fingerprints: Vec<String>,
}
impl OutputVariant for StatusV0 {
const VERSION: OutputVersion = OutputVersion::new(0, 9, 0);
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct KeySlotInfo {
fingerprint: Option<String>,
algorithm: Option<String>,
created: Option<String>,
touch_policy: Option<String>,
touch_features: Option<String>,
status: Option<String>,
pin_valid_once: bool,
public_key_material: Option<String>,
}
impl KeySlotInfo {
pub fn fingerprint(&mut self, fingerprint: String) {
self.fingerprint = Some(fingerprint);
}
pub fn algorithm(&mut self, algorithm: String) {
self.algorithm = Some(algorithm);
}
pub fn created(&mut self, created: String) {
self.created = Some(created);
}
pub fn touch_policy(&mut self, policy: String) {
self.touch_policy = Some(policy);
}
pub fn touch_features(&mut self, features: String) {
self.touch_features = Some(features);
}
pub fn status(&mut self, status: String) {
self.status = Some(status);
}
pub fn pin_valid_once(&mut self) {
self.pin_valid_once = true;
}
pub fn public_key_material(&mut self, material: String) {
self.public_key_material = Some(material);
}
fn format(&self, verbose: bool) -> Vec<String> {
let mut lines = vec![];
if let Some(fp) = &self.fingerprint {
lines.push(format!("Fingerprint: {}", fp));
}
if let Some(a) = &self.algorithm {
lines.push(format!("Algorithm: {}", a));
}
if let Some(ts) = &self.created {
lines.push(format!("Created: {}", ts));
}
if verbose {
if let Some(policy) = &self.touch_policy {
if let Some(features) = &self.touch_features {
lines.push(format!("Touch policy: {} (features: {})", policy, features));
}
}
if let Some(status) = &self.status {
lines.push(format!("Key Status: {}", status));
}
if self.pin_valid_once {
lines.push("User PIN presentation valid for one signature".into());
} else {
lines.push("User PIN presentation valid for unlimited signatures".into());
}
}
if let Some(material) = &self.public_key_material {
lines.push(format!("Public key material: {}", material));
}
lines
}
}