1use anyhow::Result;
6use cryptoki::session::Session;
7use serde::{Deserialize, Serialize};
8use serde_annotate::{Annotate, ColorProfile};
9use std::any::Any;
10use std::io::IsTerminal;
11
12use crate::module::Module;
13use crate::util::attribute::AttrData;
14
15mod aes;
16mod ecdsa;
17mod exec;
18mod kdf;
19mod object;
20mod rsa;
21mod spx;
22mod token;
23
24#[typetag::serde(tag = "command")]
25pub trait Dispatch {
26 fn run(
27 &self,
28 context: &dyn Any,
29 hsm: &Module,
30 session: Option<&Session>,
31 ) -> Result<Box<dyn erased_serde::Serialize>>;
32
33 fn leaf(&self) -> &dyn Dispatch
34 where
35 Self: Sized,
36 {
37 self
38 }
39}
40
41#[derive(clap::Subcommand, Debug, Serialize, Deserialize)]
42pub enum Commands {
43 #[command(subcommand)]
44 Aes(aes::Aes),
45 #[command(subcommand)]
46 Ecdsa(ecdsa::Ecdsa),
47 Exec(exec::Exec),
48 #[command(subcommand)]
49 Kdf(kdf::Kdf),
50 #[command(subcommand)]
51 Object(object::Object),
52 #[command(subcommand)]
53 Rsa(rsa::Rsa),
54 #[command(subcommand)]
55 Spx(spx::Spx),
56 #[command(subcommand)]
57 Token(token::Token),
58}
59
60#[typetag::serde(name = "__commands__")]
61impl Dispatch for Commands {
62 fn run(
63 &self,
64 context: &dyn Any,
65 hsm: &Module,
66 session: Option<&Session>,
67 ) -> Result<Box<dyn erased_serde::Serialize>> {
68 match self {
69 Commands::Aes(x) => x.run(context, hsm, session),
70 Commands::Ecdsa(x) => x.run(context, hsm, session),
71 Commands::Exec(x) => x.run(context, hsm, session),
72 Commands::Kdf(x) => x.run(context, hsm, session),
73 Commands::Object(x) => x.run(context, hsm, session),
74 Commands::Rsa(x) => x.run(context, hsm, session),
75 Commands::Spx(x) => x.run(context, hsm, session),
76 Commands::Token(x) => x.run(context, hsm, session),
77 }
78 }
79
80 fn leaf(&self) -> &dyn Dispatch
81 where
82 Self: Sized,
83 {
84 match self {
85 Commands::Aes(x) => x.leaf(),
86 Commands::Ecdsa(x) => x.leaf(),
87 Commands::Exec(x) => x.leaf(),
88 Commands::Kdf(x) => x.leaf(),
89 Commands::Object(x) => x.leaf(),
90 Commands::Rsa(x) => x.leaf(),
91 Commands::Spx(x) => x.leaf(),
92 Commands::Token(x) => x.leaf(),
93 }
94 }
95}
96
97#[derive(Debug, Annotate)]
98pub struct BasicResult {
99 success: bool,
100 #[serde(skip_serializing_if = "AttrData::is_none")]
101 id: AttrData,
102 #[serde(skip_serializing_if = "AttrData::is_none")]
103 label: AttrData,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 #[annotate(format = block)]
106 value: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 #[annotate(format = block)]
109 error: Option<String>,
110}
111
112#[derive(Debug, Annotate)]
113pub struct SignResult {
114 #[serde(with = "serde_bytes")]
115 #[annotate(format = hexstr)]
116 pub digest: Vec<u8>,
117 #[serde(with = "serde_bytes")]
118 #[annotate(format = hexstr)]
119 pub signature: Vec<u8>,
120}
121
122impl Default for BasicResult {
123 fn default() -> Self {
124 BasicResult {
125 success: true,
126 id: AttrData::None,
127 label: AttrData::None,
128 value: None,
129 error: None,
130 }
131 }
132}
133
134impl BasicResult {
135 pub fn from_error(e: &anyhow::Error) -> Box<dyn erased_serde::Serialize> {
136 Box::new(BasicResult {
137 success: false,
138 id: AttrData::None,
139 label: AttrData::None,
140 value: None,
141 error: Some(format!("{:?}", e)),
142 })
143 }
144}
145
146#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub enum Format {
148 Json,
149 Json5,
150 HJson,
151 Yaml,
152}
153
154pub fn print_result(
155 format: Format,
156 color: Option<bool>,
157 quiet: bool,
158 result: Result<Box<dyn erased_serde::Serialize>>,
159) -> Result<()> {
160 let (doc, result) = match result {
161 Ok(value) => {
162 let doc = serde_annotate::serialize(value.as_ref())?;
163 if quiet {
164 return Ok(());
165 }
166 (doc, Ok(()))
167 }
168 Err(e) => {
169 let doc = if let Some(exerr) = e.downcast_ref::<exec::ExecError>() {
170 exerr.result.clone()
171 } else {
172 let value = BasicResult::from_error(&e);
173 serde_annotate::serialize(value.as_ref())?
174 };
175 (doc, Err(e))
176 }
177 };
178
179 let profile = if std::io::stdout().is_terminal() && color.unwrap_or(true) {
180 ColorProfile::basic()
181 } else {
182 ColorProfile::default()
183 };
184 let string = match format {
185 Format::Json => doc.to_json().color(profile).to_string(),
186 Format::Json5 => doc.to_json5().color(profile).to_string(),
187 Format::HJson => doc.to_hjson().color(profile).to_string(),
188 Format::Yaml => doc.to_yaml().color(profile).to_string(),
189 };
190 println!("{}", string);
191 result
192}
193
194pub fn print_command(format: Format, color: Option<bool>, command: &dyn Dispatch) -> Result<()> {
195 let doc = serde_annotate::serialize(command)?;
196 let profile = if std::io::stdout().is_terminal() && color.unwrap_or(true) {
197 ColorProfile::basic()
198 } else {
199 ColorProfile::default()
200 };
201 let string = match format {
202 Format::Json => doc.to_json().color(profile).to_string(),
203 Format::Json5 => doc.to_json5().color(profile).to_string(),
204 Format::HJson => doc.to_hjson().color(profile).to_string(),
205 Format::Yaml => doc.to_yaml().color(profile).to_string(),
206 };
207 println!("{}", string);
208 Ok(())
209}