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