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