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