1use std::time::Duration;
4#[cfg(feature = "cli")]
5use std::{
6 fs::{File, read},
7 io::Read,
8 path::{Path, PathBuf},
9};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13#[cfg(feature = "cli")]
14use signstar_crypto::passphrase::Passphrase;
15use yubihsm::{
16 Capability as YubiHsmCapability,
17 command::Code,
18 object::{Filter, Id, Type},
19 opaque::Algorithm,
20 wrap::Message,
21};
22
23use crate::{
24 Credentials,
25 automation::CommandReturnValue,
26 backup::Label,
27 object::{AuthenticationKey, Capabilities, Domains, KeyInfo, ObjectId, WrapKey},
28};
29#[cfg(feature = "cli")]
30use crate::{
31 object::{WrapKeyFromPassphrase, WrapKeyKind},
32 user::FileBackedCredentials,
33};
34
35#[derive(Clone, Copy, Debug)]
37#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
38#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
39pub enum AuditOption {
40 On,
42
43 Off,
45
46 Fix,
48}
49
50impl From<AuditOption> for yubihsm::AuditOption {
51 fn from(value: AuditOption) -> Self {
52 match value {
53 AuditOption::On => Self::On,
54 AuditOption::Off => Self::Off,
55 AuditOption::Fix => Self::Fix,
56 }
57 }
58}
59
60#[derive(Debug, strum::Display)]
62#[strum(serialize_all = "snake_case")]
63#[cfg_attr(feature = "serde", derive(Serialize))]
64#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
65pub enum CommandName {
66 DeviceInfo,
68
69 ResetDeviceAndReconnect,
71
72 GetLogEntries,
74
75 SetForceAuditOption,
77
78 SetCommandAuditOption,
80
81 PutAuthenticationKey,
83
84 ChangeAuthenticationKey,
86
87 GenerateAsymmetricKey,
89
90 SignEd25519,
92
93 PutOpaque,
95
96 GetOpaque,
98
99 PutWrapKey,
101
102 ExportWrapped,
104
105 ImportWrapped,
107
108 DeleteObject,
110
111 GetObjectInfo,
113
114 ListObjects,
116}
117
118impl From<&Command> for CommandName {
119 fn from(value: &Command) -> Self {
120 match value {
121 Command::DeviceInfo => Self::DeviceInfo,
122 Command::ResetDeviceAndReconnect { .. } => Self::ResetDeviceAndReconnect,
123 Command::GetLogEntries => Self::GetLogEntries,
124 Command::SetForceAuditOption(_) => Self::SetForceAuditOption,
125 Command::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
126 Command::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
127 Command::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
128 Command::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
129 Command::SignEd25519 { .. } => Self::SignEd25519,
130 Command::PutOpaque { .. } => Self::PutOpaque,
131 Command::GetOpaque { .. } => Self::GetOpaque,
132 Command::PutWrapKey { .. } => Self::PutWrapKey,
133 Command::ExportWrapped { .. } => Self::ExportWrapped,
134 Command::ImportWrapped { .. } => Self::ImportWrapped,
135 Command::DeleteObject(_) => Self::DeleteObject,
136 Command::GetObjectInfo(_) => Self::GetObjectInfo,
137 Command::ListObjects(_) => Self::ListObjects,
138 }
139 }
140}
141
142impl From<&CommandReturnValue> for CommandName {
143 fn from(value: &CommandReturnValue) -> Self {
144 match value {
145 CommandReturnValue::DeviceInfo(_) => Self::DeviceInfo,
146 CommandReturnValue::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
147 CommandReturnValue::GetLogEntries(_) => Self::GetLogEntries,
148 CommandReturnValue::SetForceAuditOption => Self::SetForceAuditOption,
149 CommandReturnValue::SetCommandAuditOption => Self::SetCommandAuditOption,
150 CommandReturnValue::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
151 CommandReturnValue::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
152 CommandReturnValue::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
153 CommandReturnValue::SignEd25519 { .. } => Self::SignEd25519,
154 CommandReturnValue::PutOpaque { .. } => Self::PutOpaque,
155 CommandReturnValue::GetOpaque { .. } => Self::GetOpaque,
156 CommandReturnValue::PutWrapKey { .. } => Self::PutWrapKey,
157 CommandReturnValue::ExportWrapped { .. } => Self::ExportWrapped,
158 CommandReturnValue::ImportWrapped { .. } => Self::ImportWrapped,
159 CommandReturnValue::DeleteObject => Self::DeleteObject,
160 CommandReturnValue::GetObjectInfo(_) => Self::GetObjectInfo,
161 CommandReturnValue::ListObjects(_) => Self::ListObjects,
162 }
163 }
164}
165
166#[cfg(feature = "cli")]
167impl From<&FileBackedCommand> for CommandName {
168 fn from(value: &FileBackedCommand) -> Self {
169 match value {
170 FileBackedCommand::DeviceInfo => Self::DeviceInfo,
171 FileBackedCommand::ResetDeviceAndReconnect { .. } => Self::ResetDeviceAndReconnect,
172 FileBackedCommand::GetLogEntries => Self::GetLogEntries,
173 FileBackedCommand::SetForceAuditOption(_) => Self::SetForceAuditOption,
174 FileBackedCommand::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
175 FileBackedCommand::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
176 FileBackedCommand::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
177 FileBackedCommand::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
178 FileBackedCommand::SignEd25519 { .. } => Self::SignEd25519,
179 FileBackedCommand::PutOpaque { .. } => Self::PutOpaque,
180 FileBackedCommand::GetOpaque { .. } => Self::GetOpaque,
181 FileBackedCommand::PutWrapKey { .. } => Self::PutWrapKey,
182 FileBackedCommand::ExportWrapped { .. } => Self::ExportWrapped,
183 FileBackedCommand::ImportWrapped { .. } => Self::ImportWrapped,
184 FileBackedCommand::DeleteObject(_) => Self::DeleteObject,
185 FileBackedCommand::GetObjectInfo(_) => Self::GetObjectInfo,
186 FileBackedCommand::ListObjects(_) => Self::ListObjects,
187 }
188 }
189}
190
191#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
197#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
198#[derive(Clone, Copy, Debug, strum::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
199#[strum(serialize_all = "kebab-case")]
200pub enum ObjectType {
201 Opaque,
203
204 AuthenticationKey,
206
207 AsymmetricKey,
209
210 WrapKey,
212
213 HmacKey,
215
216 Template,
218
219 OtpAeakey,
221
222 SymmetricKey,
224}
225
226impl From<Type> for ObjectType {
227 fn from(value: Type) -> Self {
228 match value {
229 Type::Opaque => Self::Opaque,
230 Type::AuthenticationKey => Self::AuthenticationKey,
231 Type::AsymmetricKey => Self::AsymmetricKey,
232 Type::WrapKey => Self::WrapKey,
233 Type::HmacKey => Self::HmacKey,
234 Type::Template => Self::Template,
235 Type::OtpAeadKey => Self::OtpAeakey,
236 Type::SymmetricKey => Self::SymmetricKey,
237 }
238 }
239}
240
241impl From<&ObjectType> for Type {
242 fn from(value: &ObjectType) -> Self {
243 match value {
244 ObjectType::Opaque => Self::Opaque,
245 ObjectType::AuthenticationKey => Self::AuthenticationKey,
246 ObjectType::AsymmetricKey => Self::AsymmetricKey,
247 ObjectType::WrapKey => Self::WrapKey,
248 ObjectType::HmacKey => Self::HmacKey,
249 ObjectType::Template => Self::Template,
250 ObjectType::OtpAeakey => Self::OtpAeadKey,
251 ObjectType::SymmetricKey => Self::SymmetricKey,
252 }
253 }
254}
255
256#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
264#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
265#[derive(Clone, Debug)]
266pub enum ListObjectFilter {
267 Capabilities(Capabilities),
269
270 Domains(Domains),
272
273 Id(Id),
275
276 Type(ObjectType),
278}
279
280impl From<&ListObjectFilter> for Filter {
281 fn from(value: &ListObjectFilter) -> Self {
282 match value {
283 ListObjectFilter::Capabilities(capabilities) => {
284 Filter::Capabilities(capabilities.into())
285 }
286 ListObjectFilter::Domains(domains) => Filter::Domains(domains.into()),
287 ListObjectFilter::Id(id) => Filter::Id(*id),
288 ListObjectFilter::Type(typ) => Filter::Type(typ.into()),
289 }
290 }
291}
292
293#[derive(Clone, Debug)]
298#[cfg(feature = "cli")]
299#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
300#[cfg_attr(feature = "serde", serde(try_from = "PathBuf", into = "PathBuf"))]
301pub struct OpaqueDataFile(PathBuf);
302
303#[cfg(feature = "cli")]
304impl OpaqueDataFile {
305 pub fn new(path: impl AsRef<Path>) -> Result<Self, crate::Error> {
315 let path = path.as_ref();
316 if !path.is_file() {
317 return Err(crate::automation::Error::OpaqueDataNotAFile {
318 path: path.to_path_buf(),
319 }
320 .into());
321 }
322 let file = File::open(path).map_err(|source| crate::Error::IoPath {
323 path: path.to_path_buf(),
324 context: "opening an opaque data file for reading",
325 source,
326 })?;
327 let data_length = file
328 .metadata()
329 .map_err(|source| crate::Error::IoPath {
330 path: path.to_path_buf(),
331 context: "retrieving metadata of an opaque data file",
332 source,
333 })?
334 .len() as usize;
335 if data_length > OpaqueData::MAX_DATA_SIZE {
336 return Err(crate::automation::Error::OpaqueDataFileLength {
337 path: path.to_path_buf(),
338 data_length,
339 }
340 .into());
341 }
342
343 Ok(Self(path.to_path_buf()))
344 }
345}
346
347#[cfg(feature = "cli")]
348impl TryFrom<PathBuf> for OpaqueDataFile {
349 type Error = crate::Error;
350
351 fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
352 Self::new(&value)
353 }
354}
355
356#[cfg(feature = "cli")]
357impl From<OpaqueDataFile> for PathBuf {
358 fn from(value: OpaqueDataFile) -> Self {
359 value.0
360 }
361}
362
363#[cfg(feature = "cli")]
364impl TryFrom<&OpaqueDataFile> for Vec<u8> {
365 type Error = crate::Error;
366
367 fn try_from(value: &OpaqueDataFile) -> Result<Self, Self::Error> {
381 let mut file = File::open(value.0.as_path()).map_err(|source| crate::Error::IoPath {
382 path: value.0.clone(),
383 context: "opening an opaque data file for reading",
384 source,
385 })?;
386 let mut buffer = Vec::new();
387 file.read_to_end(&mut buffer)
388 .map_err(|source| crate::Error::IoPath {
389 path: value.0.clone(),
390 context: "reading the contents of an opaque data file",
391 source,
392 })?;
393
394 Ok(buffer)
395 }
396}
397
398#[derive(Clone, Debug)]
408pub struct OpaqueData(Vec<u8>);
409
410impl OpaqueData {
411 pub const MAX_DATA_SIZE: usize = 1980;
419
420 pub fn new(data: Vec<u8>) -> Result<Self, crate::Error> {
426 if data.len() > OpaqueData::MAX_DATA_SIZE {
427 return Err(crate::automation::Error::OpaqueDataLength {
428 data_length: data.len(),
429 }
430 .into());
431 }
432
433 Ok(Self(data))
434 }
435}
436
437#[cfg(feature = "cli")]
438impl TryFrom<&OpaqueDataFile> for OpaqueData {
439 type Error = crate::Error;
440
441 fn try_from(value: &OpaqueDataFile) -> Result<Self, Self::Error> {
442 let data: Vec<u8> = value.try_into()?;
443 Self::new(data)
444 }
445}
446
447impl From<&OpaqueData> for Vec<u8> {
448 fn from(value: &OpaqueData) -> Self {
449 value.0.clone()
450 }
451}
452
453#[derive(Clone, Copy, Debug, strum::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
461#[strum(serialize_all = "kebab-case")]
462#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
463#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
464pub enum OpaqueDataAlgorithm {
465 OpaqueData,
467
468 OpaqueX590Certificate,
470}
471
472impl From<Algorithm> for OpaqueDataAlgorithm {
473 fn from(value: Algorithm) -> Self {
474 match value {
475 Algorithm::Data => Self::OpaqueData,
476 Algorithm::X509Certificate => Self::OpaqueX590Certificate,
477 }
478 }
479}
480
481impl From<&OpaqueDataAlgorithm> for Algorithm {
482 fn from(value: &OpaqueDataAlgorithm) -> Self {
483 match value {
484 OpaqueDataAlgorithm::OpaqueData => Self::Data,
485 OpaqueDataAlgorithm::OpaqueX590Certificate => Self::X509Certificate,
486 }
487 }
488}
489
490#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
492#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
493#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
494pub enum OpaqueDataCapabilities {
495 None,
497
498 ExportableUnderWrap,
500}
501
502impl From<&OpaqueDataCapabilities> for YubiHsmCapability {
503 fn from(value: &OpaqueDataCapabilities) -> Self {
504 match value {
505 OpaqueDataCapabilities::None => YubiHsmCapability::empty(),
506 OpaqueDataCapabilities::ExportableUnderWrap => YubiHsmCapability::EXPORTABLE_UNDER_WRAP,
507 }
508 }
509}
510
511#[derive(Debug)]
513pub enum Command {
514 DeviceInfo,
516
517 ResetDeviceAndReconnect {
522 timeout: Duration,
524 },
525
526 GetLogEntries,
528
529 SetForceAuditOption(AuditOption),
536
537 SetCommandAuditOption {
544 command: Code,
546
547 setting: AuditOption,
549 },
550
551 PutAuthenticationKey {
555 info: KeyInfo,
557
558 delegated_caps: Capabilities,
561
562 authentication_key: AuthenticationKey,
564 },
565
566 ChangeAuthenticationKey {
571 key_id: Id,
573
574 authentication_key: AuthenticationKey,
576 },
577
578 GenerateAsymmetricKey {
580 info: KeyInfo,
582 },
583
584 SignEd25519 {
586 key_id: Id,
588
589 data: Vec<u8>,
591 },
592
593 PutWrapKey {
598 info: KeyInfo,
600
601 delegated_caps: Capabilities,
604
605 wrapping_key: WrapKey,
607 },
608
609 PutOpaque {
615 id: Id,
617
618 label: Label,
620
621 domains: Domains,
623
624 capabilities: OpaqueDataCapabilities,
626
627 algorithm: OpaqueDataAlgorithm,
629
630 data: OpaqueData,
632 },
633
634 GetOpaque {
636 id: Id,
638 },
639
640 ExportWrapped {
642 wrap_key_id: Id,
644
645 object: ObjectId,
647 },
648
649 ImportWrapped {
651 wrap_key_id: Id,
653
654 message: Message,
656 },
657
658 DeleteObject(ObjectId),
660
661 GetObjectInfo(ObjectId),
663
664 ListObjects(Vec<ListObjectFilter>),
666}
667
668#[cfg(feature = "cli")]
669impl TryFrom<&FileBackedCommand> for Command {
670 type Error = crate::Error;
671
672 fn try_from(value: &FileBackedCommand) -> Result<Self, Self::Error> {
678 Ok(match value {
679 FileBackedCommand::DeviceInfo => Command::DeviceInfo,
680 FileBackedCommand::ResetDeviceAndReconnect { timeout_ms } => {
681 Command::ResetDeviceAndReconnect {
682 timeout: Duration::from_millis(*timeout_ms),
683 }
684 }
685 FileBackedCommand::GetLogEntries => Command::GetLogEntries,
686 FileBackedCommand::SetForceAuditOption(audit_option) => {
687 Command::SetForceAuditOption(*audit_option)
688 }
689 FileBackedCommand::SetCommandAuditOption { command, setting } => {
690 Command::SetCommandAuditOption {
691 command: (*command),
692 setting: (*setting),
693 }
694 }
695 FileBackedCommand::PutAuthenticationKey {
696 info,
697 delegated_caps,
698 passphrase_file,
699 } => Command::PutAuthenticationKey {
700 info: info.clone(),
701 delegated_caps: delegated_caps.clone(),
702 authentication_key: AuthenticationKey::try_from(passphrase_file.as_path())?,
703 },
704 FileBackedCommand::ChangeAuthenticationKey {
705 key_id,
706 passphrase_file,
707 } => Command::ChangeAuthenticationKey {
708 key_id: *key_id,
709 authentication_key: AuthenticationKey::try_from(passphrase_file.as_path())?,
710 },
711 FileBackedCommand::GenerateAsymmetricKey { info } => {
712 Command::GenerateAsymmetricKey { info: info.clone() }
713 }
714 FileBackedCommand::SignEd25519 { key_id, data } => Command::SignEd25519 {
715 key_id: (*key_id),
716 data: data.to_vec(),
717 },
718 FileBackedCommand::PutOpaque {
719 id,
720 label,
721 domains,
722 capabilities,
723 algorithm,
724 data_file,
725 } => Command::PutOpaque {
726 id: *id,
727 label: label.clone(),
728 domains: domains.clone(),
729 capabilities: *capabilities,
730 algorithm: *algorithm,
731 data: OpaqueData::try_from(data_file)?,
732 },
733 FileBackedCommand::GetOpaque { id, .. } => Command::GetOpaque { id: *id },
734 FileBackedCommand::PutWrapKey {
735 info,
736 delegated_caps,
737 passphrase_file,
738 } => Command::PutWrapKey {
739 info: info.clone(),
740 delegated_caps: delegated_caps.clone(),
741 wrapping_key: WrapKey::try_from(WrapKeyFromPassphrase::new(
742 &Passphrase::try_from(passphrase_file.as_path())?,
743 WrapKeyKind::Aes256,
744 )?)?,
745 },
746 FileBackedCommand::ExportWrapped {
747 wrap_key_id,
748 object,
749 wrapped_file: _,
750 } => Command::ExportWrapped {
751 wrap_key_id: (*wrap_key_id),
752 object: (*object),
753 },
754 FileBackedCommand::ImportWrapped {
755 wrap_key_id,
756 wrapped_file,
757 } => {
758 let message =
759 Message::from_vec(read(wrapped_file.as_path()).map_err(|source| {
760 Self::Error::IoPath {
761 path: wrapped_file.clone(),
762 context: "reading a file under wrap",
763 source,
764 }
765 })?)
766 .map_err(|source| Self::Error::InvalidWrap {
767 context: "reading the wrapped file",
768 source,
769 })?;
770
771 Command::ImportWrapped {
772 wrap_key_id: (*wrap_key_id),
773 message,
774 }
775 }
776 FileBackedCommand::DeleteObject(id) => Command::DeleteObject(*id),
777 FileBackedCommand::GetObjectInfo(id) => Command::GetObjectInfo(*id),
778 FileBackedCommand::ListObjects(filters) => Command::ListObjects(filters.clone()),
779 })
780 }
781}
782
783#[derive(Debug)]
788#[cfg(feature = "cli")]
789#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
790#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
791pub enum FileBackedCommand {
792 DeviceInfo,
794
795 ResetDeviceAndReconnect {
803 timeout_ms: u64,
805 },
806
807 GetLogEntries,
809
810 SetForceAuditOption(AuditOption),
817
818 SetCommandAuditOption {
825 command: Code,
827
828 setting: AuditOption,
830 },
831
832 PutAuthenticationKey {
836 #[cfg_attr(feature = "serde", serde(flatten))]
838 info: KeyInfo,
839
840 delegated_caps: Capabilities,
843
844 passphrase_file: PathBuf,
846 },
847
848 ChangeAuthenticationKey {
852 key_id: Id,
854
855 passphrase_file: PathBuf,
857 },
858
859 GenerateAsymmetricKey {
861 #[cfg_attr(feature = "serde", serde(flatten))]
863 info: KeyInfo,
864 },
865
866 SignEd25519 {
868 key_id: Id,
870
871 data: Vec<u8>,
873 },
874
875 PutOpaque {
881 id: Id,
883
884 label: Label,
886
887 domains: Domains,
889
890 capabilities: OpaqueDataCapabilities,
892
893 algorithm: OpaqueDataAlgorithm,
895
896 data_file: OpaqueDataFile,
898 },
899
900 PutWrapKey {
905 #[cfg_attr(feature = "serde", serde(flatten))]
907 info: KeyInfo,
908
909 delegated_caps: Capabilities,
912
913 passphrase_file: PathBuf,
915 },
916
917 GetOpaque {
919 data_file: PathBuf,
921
922 id: Id,
924 },
925
926 ExportWrapped {
928 wrap_key_id: Id,
930
931 #[cfg_attr(feature = "serde", serde(flatten))]
933 object: ObjectId,
934
935 wrapped_file: PathBuf,
937 },
938
939 ImportWrapped {
941 wrap_key_id: Id,
943
944 wrapped_file: PathBuf,
946 },
947
948 DeleteObject(ObjectId),
950
951 GetObjectInfo(ObjectId),
953
954 ListObjects(Vec<ListObjectFilter>),
956}
957
958#[derive(Debug)]
963pub struct AuthenticatedCommandChain {
964 auth: Credentials,
965 commands: Vec<Command>,
966}
967
968impl AuthenticatedCommandChain {
969 pub fn new(auth: Credentials, commands: Vec<Command>) -> Self {
971 Self { auth, commands }
972 }
973
974 pub fn auth(&self) -> &Credentials {
976 &self.auth
977 }
978
979 pub fn commands(&self) -> &[Command] {
981 &self.commands
982 }
983}
984
985#[cfg(feature = "cli")]
990#[derive(Debug)]
991#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
992pub struct FileBackedAuthenticatedCommandChain {
993 pub(crate) auth: FileBackedCredentials,
994 pub(crate) commands: Vec<FileBackedCommand>,
995}
996
997#[cfg(test)]
998mod tests {
999 #[cfg(feature = "cli")]
1000 use std::io::Write;
1001
1002 #[cfg(feature = "cli")]
1003 use tempfile::{NamedTempFile, TempDir};
1004 use testresult::TestResult;
1005
1006 use super::*;
1007
1008 const LARGE_DATA_LENGTH: usize = OpaqueData::MAX_DATA_SIZE + 1;
1009
1010 #[test]
1012 fn opaque_data_new_fails_on_large_data() -> TestResult {
1013 let data = Vec::from_iter([1; LARGE_DATA_LENGTH]);
1014 match OpaqueData::new(data) {
1015 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataLength {
1016 ..
1017 })) => {}
1018 Err(error) => panic!(
1019 "Expected to fail with Error::OpaqueDataLength, but got a different error instead: {error}"
1020 ),
1021 Ok(opaque_data) => panic!(
1022 "Expected to fail with Error::OpaqueDataLength, succeeded instead: {opaque_data:?}"
1023 ),
1024 };
1025
1026 Ok(())
1027 }
1028
1029 #[cfg(feature = "cli")]
1031 #[test]
1032 fn path_from_opaque_data_file() -> TestResult {
1033 let data_file = {
1034 let mut data_file = NamedTempFile::new()?;
1035 let data: Vec<u8> = Vec::from_iter([1; 1]);
1036 data_file.write_all(data.as_slice())?;
1037 data_file
1038 };
1039 let opaque_data_file = OpaqueDataFile::new(data_file.path())?;
1040 let _path: PathBuf = opaque_data_file.into();
1041
1042 Ok(())
1043 }
1044
1045 #[cfg(feature = "cli")]
1047 #[test]
1048 fn opaque_data_file_new_fails_on_dir() -> TestResult {
1049 let temp_dir = TempDir::new()?;
1050
1051 match OpaqueDataFile::new(temp_dir.path()) {
1052 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataNotAFile {
1053 ..
1054 })) => {}
1055 Err(error) => panic!(
1056 "Expected to fail with Error::OpaqueDataNotAFile, but got a different error instead: {error}"
1057 ),
1058 Ok(opaque_data) => panic!(
1059 "Expected to fail with Error::OpaqueDataNotAFile, succeeded instead: {opaque_data:?}"
1060 ),
1061 };
1062
1063 Ok(())
1064 }
1065
1066 #[cfg(feature = "cli")]
1068 #[test]
1069 fn opaque_data_file_new_fails_on_large_data() -> TestResult {
1070 let data_file = {
1071 let mut data_file = NamedTempFile::new()?;
1072 let data: Vec<u8> = Vec::from_iter([1; LARGE_DATA_LENGTH]);
1073 data_file.write_all(data.as_slice())?;
1074 data_file
1075 };
1076
1077 match OpaqueDataFile::new(data_file.path()) {
1078 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataFileLength {
1079 ..
1080 })) => {}
1081 Err(error) => panic!(
1082 "Expected to fail with Error::OpaqueDataFileLength, but got a different error instead: {error}"
1083 ),
1084 Ok(opaque_data) => panic!(
1085 "Expected to fail with Error::OpaqueDataFileLength, succeeded instead: {opaque_data:?}"
1086 ),
1087 };
1088
1089 Ok(())
1090 }
1091}