1#[cfg(feature = "cli")]
4use std::{fs::read, path::PathBuf};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8#[cfg(feature = "cli")]
9use signstar_crypto::passphrase::Passphrase;
10use yubihsm::{command::Code, wrap::Message};
11
12use crate::{
13 Credentials,
14 automation::CommandReturnValue,
15 object::{AuthenticationKey, Capabilities, Id, KeyInfo, ObjectId, WrapKey},
16};
17#[cfg(feature = "cli")]
18use crate::{
19 object::{WrapKeyFromPassphrase, WrapKeyKind},
20 user::FileBackedCredentials,
21};
22
23#[derive(Clone, Copy, Debug)]
25#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
26#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
27pub enum AuditOption {
28 On,
30
31 Off,
33
34 Fix,
36}
37
38impl From<AuditOption> for yubihsm::AuditOption {
39 fn from(value: AuditOption) -> Self {
40 match value {
41 AuditOption::On => Self::On,
42 AuditOption::Off => Self::Off,
43 AuditOption::Fix => Self::Fix,
44 }
45 }
46}
47
48#[derive(Debug, strum::Display)]
50#[strum(serialize_all = "snake_case")]
51#[cfg_attr(feature = "serde", derive(Serialize))]
52#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
53pub enum CommandName {
54 DeviceInfo,
56
57 ResetDeviceAndReconnect,
59
60 GetLogEntries,
62
63 SetForceAuditOption,
65
66 SetCommandAuditOption,
68
69 PutAuthenticationKey,
71
72 GenerateAsymmetricKey,
74
75 SignEd25519,
77
78 PutWrapKey,
80
81 ExportWrapped,
83
84 ImportWrapped,
86
87 DeleteObject,
89
90 GetObjectInfo,
92}
93
94impl From<&Command> for CommandName {
95 fn from(value: &Command) -> Self {
96 match value {
97 Command::DeviceInfo => Self::DeviceInfo,
98 Command::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
99 Command::GetLogEntries => Self::GetLogEntries,
100 Command::SetForceAuditOption(_) => Self::SetForceAuditOption,
101 Command::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
102 Command::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
103 Command::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
104 Command::SignEd25519 { .. } => Self::SignEd25519,
105 Command::PutWrapKey { .. } => Self::PutWrapKey,
106 Command::ExportWrapped { .. } => Self::ExportWrapped,
107 Command::ImportWrapped { .. } => Self::ImportWrapped,
108 Command::DeleteObject(_) => Self::DeleteObject,
109 Command::GetObjectInfo(_) => Self::GetObjectInfo,
110 }
111 }
112}
113
114impl From<&CommandReturnValue> for CommandName {
115 fn from(value: &CommandReturnValue) -> Self {
116 match value {
117 CommandReturnValue::DeviceInfo(_) => Self::DeviceInfo,
118 CommandReturnValue::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
119 CommandReturnValue::GetLogEntries(_) => Self::GetLogEntries,
120 CommandReturnValue::SetForceAuditOption => Self::SetForceAuditOption,
121 CommandReturnValue::SetCommandAuditOption => Self::SetCommandAuditOption,
122 CommandReturnValue::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
123 CommandReturnValue::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
124 CommandReturnValue::SignEd25519 { .. } => Self::SignEd25519,
125 CommandReturnValue::PutWrapKey { .. } => Self::PutWrapKey,
126 CommandReturnValue::ExportWrapped { .. } => Self::ExportWrapped,
127 CommandReturnValue::ImportWrapped { .. } => Self::ImportWrapped,
128 CommandReturnValue::DeleteObject => Self::DeleteObject,
129 CommandReturnValue::GetObjectInfo(_) => Self::GetObjectInfo,
130 }
131 }
132}
133
134#[cfg(feature = "cli")]
135impl From<&FileBackedCommand> for CommandName {
136 fn from(value: &FileBackedCommand) -> Self {
137 match value {
138 FileBackedCommand::DeviceInfo => Self::DeviceInfo,
139 FileBackedCommand::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
140 FileBackedCommand::GetLogEntries => Self::GetLogEntries,
141 FileBackedCommand::SetForceAuditOption(_) => Self::SetForceAuditOption,
142 FileBackedCommand::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
143 FileBackedCommand::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
144 FileBackedCommand::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
145 FileBackedCommand::SignEd25519 { .. } => Self::SignEd25519,
146 FileBackedCommand::PutWrapKey { .. } => Self::PutWrapKey,
147 FileBackedCommand::ExportWrapped { .. } => Self::ExportWrapped,
148 FileBackedCommand::ImportWrapped { .. } => Self::ImportWrapped,
149 FileBackedCommand::DeleteObject(_) => Self::DeleteObject,
150 FileBackedCommand::GetObjectInfo(_) => Self::GetObjectInfo,
151 }
152 }
153}
154
155#[derive(Debug)]
157pub enum Command {
158 DeviceInfo,
160
161 ResetDeviceAndReconnect,
166
167 GetLogEntries,
169
170 SetForceAuditOption(AuditOption),
177
178 #[allow(clippy::enum_variant_names)]
185 SetCommandAuditOption {
186 command: Code,
188
189 setting: AuditOption,
191 },
192
193 PutAuthenticationKey {
197 info: KeyInfo,
199
200 delegated_caps: Capabilities,
203
204 authentication_key: AuthenticationKey,
206 },
207
208 GenerateAsymmetricKey {
210 info: KeyInfo,
212 },
213
214 SignEd25519 {
216 key_id: Id,
218
219 data: Vec<u8>,
221 },
222
223 PutWrapKey {
228 info: KeyInfo,
230
231 delegated_caps: Capabilities,
234
235 wrapping_key: WrapKey,
237 },
238
239 ExportWrapped {
241 wrap_key_id: Id,
243
244 object: ObjectId,
246 },
247
248 ImportWrapped {
250 wrap_key_id: Id,
252
253 message: Message,
255 },
256
257 DeleteObject(ObjectId),
259
260 GetObjectInfo(ObjectId),
262}
263
264#[cfg(feature = "cli")]
265impl TryFrom<&FileBackedCommand> for Command {
266 type Error = crate::Error;
267
268 fn try_from(value: &FileBackedCommand) -> Result<Self, Self::Error> {
274 Ok(match value {
275 FileBackedCommand::DeviceInfo => Command::DeviceInfo,
276 FileBackedCommand::ResetDeviceAndReconnect => Command::ResetDeviceAndReconnect,
277 FileBackedCommand::GetLogEntries => Command::GetLogEntries,
278 FileBackedCommand::SetForceAuditOption(audit_option) => {
279 Command::SetForceAuditOption(*audit_option)
280 }
281 FileBackedCommand::SetCommandAuditOption { command, setting } => {
282 Command::SetCommandAuditOption {
283 command: (*command),
284 setting: (*setting),
285 }
286 }
287 FileBackedCommand::PutAuthenticationKey {
288 info,
289 delegated_caps,
290 passphrase_file,
291 } => Command::PutAuthenticationKey {
292 info: info.clone(),
293 delegated_caps: delegated_caps.clone(),
294 authentication_key: AuthenticationKey::try_from(passphrase_file.as_path())?,
295 },
296 FileBackedCommand::GenerateAsymmetricKey { info } => {
297 Command::GenerateAsymmetricKey { info: info.clone() }
298 }
299 FileBackedCommand::SignEd25519 { key_id, data } => Command::SignEd25519 {
300 key_id: (*key_id),
301 data: data.to_vec(),
302 },
303 FileBackedCommand::PutWrapKey {
304 info,
305 delegated_caps,
306 passphrase_file,
307 } => Command::PutWrapKey {
308 info: info.clone(),
309 delegated_caps: delegated_caps.clone(),
310 wrapping_key: WrapKey::try_from(WrapKeyFromPassphrase::new(
311 &Passphrase::try_from(passphrase_file.as_path())?,
312 WrapKeyKind::Aes256,
313 )?)?,
314 },
315 FileBackedCommand::ExportWrapped {
316 wrap_key_id,
317 object,
318 wrapped_file: _,
319 } => Command::ExportWrapped {
320 wrap_key_id: (*wrap_key_id),
321 object: (*object),
322 },
323 FileBackedCommand::ImportWrapped {
324 wrap_key_id,
325 wrapped_file,
326 } => {
327 let message =
328 Message::from_vec(read(wrapped_file.as_path()).map_err(|source| {
329 Self::Error::IoPath {
330 path: wrapped_file.clone(),
331 context: "reading a file under wrap",
332 source,
333 }
334 })?)
335 .map_err(|source| Self::Error::InvalidWrap {
336 context: "reading the wrapped file",
337 source,
338 })?;
339
340 Command::ImportWrapped {
341 wrap_key_id: (*wrap_key_id),
342 message,
343 }
344 }
345 FileBackedCommand::DeleteObject(id) => Command::DeleteObject(*id),
346 FileBackedCommand::GetObjectInfo(id) => Command::GetObjectInfo(*id),
347 })
348 }
349}
350
351#[derive(Debug)]
356#[cfg(feature = "cli")]
357#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
358#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
359pub enum FileBackedCommand {
360 DeviceInfo,
362
363 ResetDeviceAndReconnect,
368
369 GetLogEntries,
371
372 SetForceAuditOption(AuditOption),
379
380 #[allow(clippy::enum_variant_names)]
387 SetCommandAuditOption {
388 command: Code,
390
391 setting: AuditOption,
393 },
394
395 PutAuthenticationKey {
399 #[cfg_attr(feature = "serde", serde(flatten))]
401 info: KeyInfo,
402
403 delegated_caps: Capabilities,
406
407 passphrase_file: PathBuf,
409 },
410
411 GenerateAsymmetricKey {
413 #[cfg_attr(feature = "serde", serde(flatten))]
415 info: KeyInfo,
416 },
417
418 SignEd25519 {
420 key_id: Id,
422
423 data: Vec<u8>,
425 },
426
427 PutWrapKey {
432 #[cfg_attr(feature = "serde", serde(flatten))]
434 info: KeyInfo,
435
436 delegated_caps: Capabilities,
439
440 passphrase_file: PathBuf,
442 },
443
444 ExportWrapped {
446 wrap_key_id: Id,
448
449 #[cfg_attr(feature = "serde", serde(flatten))]
451 object: ObjectId,
452
453 wrapped_file: PathBuf,
455 },
456
457 ImportWrapped {
459 wrap_key_id: Id,
461
462 wrapped_file: PathBuf,
464 },
465
466 DeleteObject(ObjectId),
468
469 GetObjectInfo(ObjectId),
471}
472
473#[derive(Debug)]
478pub struct AuthenticatedCommandChain {
479 auth: Credentials,
480 commands: Vec<Command>,
481}
482
483impl AuthenticatedCommandChain {
484 pub fn new(auth: Credentials, commands: Vec<Command>) -> Self {
486 Self { auth, commands }
487 }
488
489 pub fn auth(&self) -> &Credentials {
491 &self.auth
492 }
493
494 pub fn commands(&self) -> &[Command] {
496 &self.commands
497 }
498}
499
500#[cfg(feature = "cli")]
505#[derive(Debug)]
506#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
507pub struct FileBackedAuthenticatedCommandChain {
508 pub(crate) auth: FileBackedCredentials,
509 pub(crate) commands: Vec<FileBackedCommand>,
510}