1#![allow(clippy::doc_markdown)]
6
7pub mod algorithm;
8pub mod cache;
9pub mod certificate;
10pub mod common;
11pub mod convert;
12pub mod create;
13pub mod create_primary;
14pub mod delete;
15pub mod evict;
16pub mod load;
17pub mod memory;
18pub mod pcr_event;
19pub mod policy;
20pub mod reset_lock;
21pub mod return_code;
22pub mod unseal;
23
24pub use algorithm::*;
25pub use cache::*;
26pub use certificate::*;
27pub use common::*;
28pub use convert::*;
29pub use create::*;
30pub use create_primary::*;
31pub use delete::*;
32pub use evict::*;
33pub use load::*;
34pub use memory::*;
35pub use pcr_event::*;
36pub use policy::*;
37pub use reset_lock::*;
38pub use return_code::*;
39pub use unseal::*;
40
41use crate::{
42 auth::AuthError,
43 crypto::CryptoError,
44 device::DeviceError,
45 handle::{HandleError, HandlePatternError},
46 job::JobError,
47 key::{AlgInfo, KeyError},
48 pcr::PcrError,
49 policy::PolicyError,
50 vtpm::VtpmError,
51};
52use clap::builder::styling::Style as AnsiStyle;
53use std::{
54 io::{IsTerminal, Write},
55 num::TryFromIntError,
56};
57use thiserror::Error;
58use tpm2_protocol::{
59 data::{TpmCc, TpmRcBase},
60 TpmError,
61};
62
63pub trait Tabled {
65 fn headers() -> Vec<String>;
67 fn row(&self) -> Vec<String>;
69}
70
71pub fn print_table<T>(writer: &mut dyn Write, items: &[T]) -> Result<(), std::io::Error>
77where
78 T: Tabled,
79{
80 if items.is_empty() {
81 return Ok(());
82 }
83
84 let headers = T::headers();
85 let rows: Vec<Vec<String>> = items.iter().map(T::row).collect();
86 let num_columns = headers.len();
87 let mut max_widths = vec![0; num_columns];
88
89 for i in 0..num_columns {
90 max_widths[i] = headers[i].len();
91 }
92 for row in &rows {
93 for (i, cell) in row.iter().enumerate() {
94 if cell.len() > max_widths[i] {
95 max_widths[i] = cell.len();
96 }
97 }
98 }
99
100 let bold = AnsiStyle::new().bold();
101 let header_line = headers
102 .iter()
103 .zip(&max_widths)
104 .map(|(header, &width)| format!("{header:<width$}"))
105 .collect::<Vec<String>>()
106 .join(" ");
107
108 if std::io::stdout().is_terminal() {
109 writeln!(writer, "{bold}{header_line}{bold:#}")?;
110 } else {
111 writeln!(writer, "{header_line}")?;
112 }
113
114 for row in &rows {
115 let row_line = row
116 .iter()
117 .zip(&max_widths)
118 .map(|(cell, &width)| format!("{cell:<width$}"))
119 .collect::<Vec<String>>()
120 .join(" ");
121 writeln!(writer, "{row_line}")?;
122 }
123
124 Ok(())
125}
126
127pub fn deny_keyedhash(algorithm: &crate::key::Alg) -> Result<(), CommandError> {
133 if algorithm.params == AlgInfo::KeyedHash {
134 Err(CommandError::UnsupportedKeyAlgorithm(algorithm.clone()))
135 } else {
136 Ok(())
137 }
138}
139
140#[derive(Debug, Error)]
141pub enum CommandError {
142 #[error("authentication denied")]
143 AuthenticationDenied,
144 #[error("dictionary attack lockout is active")]
145 DictionaryAttackLocked,
146 #[error("invalid key format")]
147 InvalidFormat,
148 #[error("invalid input: {0}")]
149 InvalidInput(String),
150 #[error("invalid output: {0}")]
151 InvalidOutput(String),
152 #[error("invalid parent: {0}{1:08x}")]
153 InvalidParent(&'static str, u32),
154 #[error("parent missing")]
155 ParentMissing,
156 #[error("response mismatch: {0}")]
157 ResponseMismatch(TpmCc),
158 #[error("sensitive data denied")]
159 SensitiveDataDenied,
160 #[error("sensitive data missing")]
161 SensitiveDataMissing,
162 #[error("unknown parent")]
163 UnknownParent,
164 #[error("unsupported key algorithm: '{0}'")]
165 UnsupportedKeyAlgorithm(crate::key::Alg),
166 #[error("auth: {0}")]
167 Auth(#[from] AuthError),
168 #[error("cache: {0}")]
169 Cache(VtpmError),
170 #[error("job: {0}")]
171 Job(JobError),
172 #[error("device: {0}")]
173 Device(DeviceError),
174 #[error("crypto: {0}")]
175 Crypto(#[from] CryptoError),
176 #[error("handle: {0}")]
177 Handle(#[from] HandleError),
178 #[error("handle pattern: {0}")]
179 HandlePattern(#[from] HandlePatternError),
180 #[error("key error: {0}")]
181 Key(#[from] KeyError),
182 #[error("pcr: {0}")]
183 Pcr(#[from] PcrError),
184 #[error("policy: {0}")]
185 Policy(#[from] PolicyError),
186 #[error("hex decode: {0}")]
187 HexDecode(#[from] hex::FromHexError),
188 #[error("int decode: {0}")]
189 IntDecode(#[from] TryFromIntError),
190 #[error("I/O: {0}")]
191 Io(#[from] std::io::Error),
192 #[error("protocol: {0}")]
193 TpmProtocol(TpmError),
194}
195
196impl From<JobError> for CommandError {
197 fn from(err: JobError) -> Self {
198 match err {
199 JobError::InvalidParent(prefix, handle) => Self::InvalidParent(prefix, handle),
200 JobError::Device(dev_err) => Self::from(dev_err),
201 _ => Self::Job(err),
202 }
203 }
204}
205
206impl From<VtpmError> for CommandError {
207 fn from(err: VtpmError) -> Self {
208 Self::Cache(err)
209 }
210}
211
212impl From<DeviceError> for CommandError {
213 fn from(err: DeviceError) -> Self {
214 if let DeviceError::TpmRc(rc) = &err {
215 let base = rc.base();
216 if base == TpmRcBase::AuthFail || base == TpmRcBase::AuthMissing {
217 return Self::AuthenticationDenied;
218 }
219 if base == TpmRcBase::Lockout {
220 return Self::DictionaryAttackLocked;
221 }
222 }
223 Self::Device(err)
224 }
225}
226
227impl From<TpmError> for CommandError {
228 fn from(err: TpmError) -> Self {
229 Self::TpmProtocol(err)
230 }
231}