1#[cfg(feature = "cli")]
4use std::{
5 fs::{File, read},
6 io::Read,
7 path::{Path, PathBuf},
8};
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12#[cfg(feature = "cli")]
13use signstar_crypto::passphrase::Passphrase;
14use yubihsm::{
15 Capability as YubiHsmCapability,
16 command::Code,
17 object::{Filter, Id, Type},
18 opaque::Algorithm,
19 wrap::Message,
20};
21
22use crate::{
23 Credentials,
24 automation::CommandReturnValue,
25 backup::Label,
26 object::{AuthenticationKey, Capabilities, Domains, KeyInfo, ObjectId, WrapKey},
27};
28#[cfg(feature = "cli")]
29use crate::{
30 object::{WrapKeyFromPassphrase, WrapKeyKind},
31 user::FileBackedCredentials,
32};
33
34#[derive(Clone, Copy, Debug)]
36#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
37#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
38pub enum AuditOption {
39 On,
41
42 Off,
44
45 Fix,
47}
48
49impl From<AuditOption> for yubihsm::AuditOption {
50 fn from(value: AuditOption) -> Self {
51 match value {
52 AuditOption::On => Self::On,
53 AuditOption::Off => Self::Off,
54 AuditOption::Fix => Self::Fix,
55 }
56 }
57}
58
59#[derive(Debug, strum::Display)]
61#[strum(serialize_all = "snake_case")]
62#[cfg_attr(feature = "serde", derive(Serialize))]
63#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
64pub enum CommandName {
65 DeviceInfo,
67
68 ResetDeviceAndReconnect,
70
71 GetLogEntries,
73
74 SetForceAuditOption,
76
77 SetCommandAuditOption,
79
80 PutAuthenticationKey,
82
83 ChangeAuthenticationKey,
85
86 GenerateAsymmetricKey,
88
89 SignEd25519,
91
92 PutOpaque,
94
95 GetOpaque,
97
98 PutWrapKey,
100
101 ExportWrapped,
103
104 ImportWrapped,
106
107 DeleteObject,
109
110 GetObjectInfo,
112
113 ListObjects,
115}
116
117impl From<&Command> for CommandName {
118 fn from(value: &Command) -> Self {
119 match value {
120 Command::DeviceInfo => Self::DeviceInfo,
121 Command::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
122 Command::GetLogEntries => Self::GetLogEntries,
123 Command::SetForceAuditOption(_) => Self::SetForceAuditOption,
124 Command::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
125 Command::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
126 Command::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
127 Command::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
128 Command::SignEd25519 { .. } => Self::SignEd25519,
129 Command::PutOpaque { .. } => Self::PutOpaque,
130 Command::GetOpaque { .. } => Self::GetOpaque,
131 Command::PutWrapKey { .. } => Self::PutWrapKey,
132 Command::ExportWrapped { .. } => Self::ExportWrapped,
133 Command::ImportWrapped { .. } => Self::ImportWrapped,
134 Command::DeleteObject(_) => Self::DeleteObject,
135 Command::GetObjectInfo(_) => Self::GetObjectInfo,
136 Command::ListObjects(_) => Self::ListObjects,
137 }
138 }
139}
140
141impl From<&CommandReturnValue> for CommandName {
142 fn from(value: &CommandReturnValue) -> Self {
143 match value {
144 CommandReturnValue::DeviceInfo(_) => Self::DeviceInfo,
145 CommandReturnValue::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
146 CommandReturnValue::GetLogEntries(_) => Self::GetLogEntries,
147 CommandReturnValue::SetForceAuditOption => Self::SetForceAuditOption,
148 CommandReturnValue::SetCommandAuditOption => Self::SetCommandAuditOption,
149 CommandReturnValue::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
150 CommandReturnValue::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
151 CommandReturnValue::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
152 CommandReturnValue::SignEd25519 { .. } => Self::SignEd25519,
153 CommandReturnValue::PutOpaque { .. } => Self::PutOpaque,
154 CommandReturnValue::GetOpaque { .. } => Self::GetOpaque,
155 CommandReturnValue::PutWrapKey { .. } => Self::PutWrapKey,
156 CommandReturnValue::ExportWrapped { .. } => Self::ExportWrapped,
157 CommandReturnValue::ImportWrapped { .. } => Self::ImportWrapped,
158 CommandReturnValue::DeleteObject => Self::DeleteObject,
159 CommandReturnValue::GetObjectInfo(_) => Self::GetObjectInfo,
160 CommandReturnValue::ListObjects(_) => Self::ListObjects,
161 }
162 }
163}
164
165#[cfg(feature = "cli")]
166impl From<&FileBackedCommand> for CommandName {
167 fn from(value: &FileBackedCommand) -> Self {
168 match value {
169 FileBackedCommand::DeviceInfo => Self::DeviceInfo,
170 FileBackedCommand::ResetDeviceAndReconnect => Self::ResetDeviceAndReconnect,
171 FileBackedCommand::GetLogEntries => Self::GetLogEntries,
172 FileBackedCommand::SetForceAuditOption(_) => Self::SetForceAuditOption,
173 FileBackedCommand::SetCommandAuditOption { .. } => Self::SetCommandAuditOption,
174 FileBackedCommand::PutAuthenticationKey { .. } => Self::PutAuthenticationKey,
175 FileBackedCommand::ChangeAuthenticationKey { .. } => Self::ChangeAuthenticationKey,
176 FileBackedCommand::GenerateAsymmetricKey { .. } => Self::GenerateAsymmetricKey,
177 FileBackedCommand::SignEd25519 { .. } => Self::SignEd25519,
178 FileBackedCommand::PutOpaque { .. } => Self::PutOpaque,
179 FileBackedCommand::GetOpaque { .. } => Self::GetOpaque,
180 FileBackedCommand::PutWrapKey { .. } => Self::PutWrapKey,
181 FileBackedCommand::ExportWrapped { .. } => Self::ExportWrapped,
182 FileBackedCommand::ImportWrapped { .. } => Self::ImportWrapped,
183 FileBackedCommand::DeleteObject(_) => Self::DeleteObject,
184 FileBackedCommand::GetObjectInfo(_) => Self::GetObjectInfo,
185 FileBackedCommand::ListObjects(_) => Self::ListObjects,
186 }
187 }
188}
189
190#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
196#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
197#[derive(Clone, Copy, Debug, strum::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
198#[strum(serialize_all = "kebab-case")]
199pub enum ObjectType {
200 Opaque,
202
203 AuthenticationKey,
205
206 AsymmetricKey,
208
209 WrapKey,
211
212 HmacKey,
214
215 Template,
217
218 OtpAeakey,
220
221 SymmetricKey,
223}
224
225impl From<Type> for ObjectType {
226 fn from(value: Type) -> Self {
227 match value {
228 Type::Opaque => Self::Opaque,
229 Type::AuthenticationKey => Self::AuthenticationKey,
230 Type::AsymmetricKey => Self::AsymmetricKey,
231 Type::WrapKey => Self::WrapKey,
232 Type::HmacKey => Self::HmacKey,
233 Type::Template => Self::Template,
234 Type::OtpAeadKey => Self::OtpAeakey,
235 Type::SymmetricKey => Self::SymmetricKey,
236 }
237 }
238}
239
240impl From<&ObjectType> for Type {
241 fn from(value: &ObjectType) -> Self {
242 match value {
243 ObjectType::Opaque => Self::Opaque,
244 ObjectType::AuthenticationKey => Self::AuthenticationKey,
245 ObjectType::AsymmetricKey => Self::AsymmetricKey,
246 ObjectType::WrapKey => Self::WrapKey,
247 ObjectType::HmacKey => Self::HmacKey,
248 ObjectType::Template => Self::Template,
249 ObjectType::OtpAeakey => Self::OtpAeadKey,
250 ObjectType::SymmetricKey => Self::SymmetricKey,
251 }
252 }
253}
254
255#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
263#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
264#[derive(Clone, Debug)]
265pub enum ListObjectFilter {
266 Capabilities(Capabilities),
268
269 Domains(Domains),
271
272 Id(Id),
274
275 Type(ObjectType),
277}
278
279impl From<&ListObjectFilter> for Filter {
280 fn from(value: &ListObjectFilter) -> Self {
281 match value {
282 ListObjectFilter::Capabilities(capabilities) => {
283 Filter::Capabilities(capabilities.into())
284 }
285 ListObjectFilter::Domains(domains) => Filter::Domains(domains.into()),
286 ListObjectFilter::Id(id) => Filter::Id(*id),
287 ListObjectFilter::Type(typ) => Filter::Type(typ.into()),
288 }
289 }
290}
291
292#[derive(Clone, Debug)]
297#[cfg(feature = "cli")]
298#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
299#[cfg_attr(feature = "serde", serde(try_from = "PathBuf", into = "PathBuf"))]
300pub struct OpaqueDataFile(PathBuf);
301
302#[cfg(feature = "cli")]
303impl OpaqueDataFile {
304 pub fn new(path: impl AsRef<Path>) -> Result<Self, crate::Error> {
314 let path = path.as_ref();
315 if !path.is_file() {
316 return Err(crate::automation::Error::OpaqueDataNotAFile {
317 path: path.to_path_buf(),
318 }
319 .into());
320 }
321 let file = File::open(path).map_err(|source| crate::Error::IoPath {
322 path: path.to_path_buf(),
323 context: "opening an opaque data file for reading",
324 source,
325 })?;
326 let data_length = file
327 .metadata()
328 .map_err(|source| crate::Error::IoPath {
329 path: path.to_path_buf(),
330 context: "retrieving metadata of an opaque data file",
331 source,
332 })?
333 .len() as usize;
334 if data_length > OpaqueData::MAX_DATA_SIZE {
335 return Err(crate::automation::Error::OpaqueDataFileLength {
336 path: path.to_path_buf(),
337 data_length,
338 }
339 .into());
340 }
341
342 Ok(Self(path.to_path_buf()))
343 }
344}
345
346#[cfg(feature = "cli")]
347impl TryFrom<PathBuf> for OpaqueDataFile {
348 type Error = crate::Error;
349
350 fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
351 Self::new(&value)
352 }
353}
354
355#[cfg(feature = "cli")]
356impl From<OpaqueDataFile> for PathBuf {
357 fn from(value: OpaqueDataFile) -> Self {
358 value.0
359 }
360}
361
362#[cfg(feature = "cli")]
363impl TryFrom<&OpaqueDataFile> for Vec<u8> {
364 type Error = crate::Error;
365
366 fn try_from(value: &OpaqueDataFile) -> Result<Self, Self::Error> {
380 let mut file = File::open(value.0.as_path()).map_err(|source| crate::Error::IoPath {
381 path: value.0.clone(),
382 context: "opening an opaque data file for reading",
383 source,
384 })?;
385 let mut buffer = Vec::new();
386 file.read_to_end(&mut buffer)
387 .map_err(|source| crate::Error::IoPath {
388 path: value.0.clone(),
389 context: "reading the contents of an opaque data file",
390 source,
391 })?;
392
393 Ok(buffer)
394 }
395}
396
397#[derive(Clone, Debug)]
407pub struct OpaqueData(Vec<u8>);
408
409impl OpaqueData {
410 pub const MAX_DATA_SIZE: usize = 1980;
418
419 pub fn new(data: Vec<u8>) -> Result<Self, crate::Error> {
425 if data.len() > OpaqueData::MAX_DATA_SIZE {
426 return Err(crate::automation::Error::OpaqueDataLength {
427 data_length: data.len(),
428 }
429 .into());
430 }
431
432 Ok(Self(data))
433 }
434}
435
436#[cfg(feature = "cli")]
437impl TryFrom<&OpaqueDataFile> for OpaqueData {
438 type Error = crate::Error;
439
440 fn try_from(value: &OpaqueDataFile) -> Result<Self, Self::Error> {
441 let data: Vec<u8> = value.try_into()?;
442 Self::new(data)
443 }
444}
445
446impl From<&OpaqueData> for Vec<u8> {
447 fn from(value: &OpaqueData) -> Self {
448 value.0.clone()
449 }
450}
451
452#[derive(Clone, Copy, Debug, strum::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
460#[strum(serialize_all = "kebab-case")]
461#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
462#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
463pub enum OpaqueDataAlgorithm {
464 OpaqueData,
466
467 OpaqueX590Certificate,
469}
470
471impl From<Algorithm> for OpaqueDataAlgorithm {
472 fn from(value: Algorithm) -> Self {
473 match value {
474 Algorithm::Data => Self::OpaqueData,
475 Algorithm::X509Certificate => Self::OpaqueX590Certificate,
476 }
477 }
478}
479
480impl From<&OpaqueDataAlgorithm> for Algorithm {
481 fn from(value: &OpaqueDataAlgorithm) -> Self {
482 match value {
483 OpaqueDataAlgorithm::OpaqueData => Self::Data,
484 OpaqueDataAlgorithm::OpaqueX590Certificate => Self::X509Certificate,
485 }
486 }
487}
488
489#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
491#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
492#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
493pub enum OpaqueDataCapabilities {
494 None,
496
497 ExportableUnderWrap,
499}
500
501impl From<&OpaqueDataCapabilities> for YubiHsmCapability {
502 fn from(value: &OpaqueDataCapabilities) -> Self {
503 match value {
504 OpaqueDataCapabilities::None => YubiHsmCapability::empty(),
505 OpaqueDataCapabilities::ExportableUnderWrap => YubiHsmCapability::EXPORTABLE_UNDER_WRAP,
506 }
507 }
508}
509
510#[derive(Debug)]
512pub enum Command {
513 DeviceInfo,
515
516 ResetDeviceAndReconnect,
521
522 GetLogEntries,
524
525 SetForceAuditOption(AuditOption),
532
533 SetCommandAuditOption {
540 command: Code,
542
543 setting: AuditOption,
545 },
546
547 PutAuthenticationKey {
551 info: KeyInfo,
553
554 delegated_caps: Capabilities,
557
558 authentication_key: AuthenticationKey,
560 },
561
562 ChangeAuthenticationKey {
567 key_id: Id,
569
570 authentication_key: AuthenticationKey,
572 },
573
574 GenerateAsymmetricKey {
576 info: KeyInfo,
578 },
579
580 SignEd25519 {
582 key_id: Id,
584
585 data: Vec<u8>,
587 },
588
589 PutWrapKey {
594 info: KeyInfo,
596
597 delegated_caps: Capabilities,
600
601 wrapping_key: WrapKey,
603 },
604
605 PutOpaque {
611 id: Id,
613
614 label: Label,
616
617 domains: Domains,
619
620 capabilities: OpaqueDataCapabilities,
622
623 algorithm: OpaqueDataAlgorithm,
625
626 data: OpaqueData,
628 },
629
630 GetOpaque {
632 id: Id,
634 },
635
636 ExportWrapped {
638 wrap_key_id: Id,
640
641 object: ObjectId,
643 },
644
645 ImportWrapped {
647 wrap_key_id: Id,
649
650 message: Message,
652 },
653
654 DeleteObject(ObjectId),
656
657 GetObjectInfo(ObjectId),
659
660 ListObjects(Vec<ListObjectFilter>),
662}
663
664#[cfg(feature = "cli")]
665impl TryFrom<&FileBackedCommand> for Command {
666 type Error = crate::Error;
667
668 fn try_from(value: &FileBackedCommand) -> Result<Self, Self::Error> {
674 Ok(match value {
675 FileBackedCommand::DeviceInfo => Command::DeviceInfo,
676 FileBackedCommand::ResetDeviceAndReconnect => Command::ResetDeviceAndReconnect,
677 FileBackedCommand::GetLogEntries => Command::GetLogEntries,
678 FileBackedCommand::SetForceAuditOption(audit_option) => {
679 Command::SetForceAuditOption(*audit_option)
680 }
681 FileBackedCommand::SetCommandAuditOption { command, setting } => {
682 Command::SetCommandAuditOption {
683 command: (*command),
684 setting: (*setting),
685 }
686 }
687 FileBackedCommand::PutAuthenticationKey {
688 info,
689 delegated_caps,
690 passphrase_file,
691 } => Command::PutAuthenticationKey {
692 info: info.clone(),
693 delegated_caps: delegated_caps.clone(),
694 authentication_key: AuthenticationKey::try_from(passphrase_file.as_path())?,
695 },
696 FileBackedCommand::ChangeAuthenticationKey {
697 key_id,
698 passphrase_file,
699 } => Command::ChangeAuthenticationKey {
700 key_id: *key_id,
701 authentication_key: AuthenticationKey::try_from(passphrase_file.as_path())?,
702 },
703 FileBackedCommand::GenerateAsymmetricKey { info } => {
704 Command::GenerateAsymmetricKey { info: info.clone() }
705 }
706 FileBackedCommand::SignEd25519 { key_id, data } => Command::SignEd25519 {
707 key_id: (*key_id),
708 data: data.to_vec(),
709 },
710 FileBackedCommand::PutOpaque {
711 id,
712 label,
713 domains,
714 capabilities,
715 algorithm,
716 data_file,
717 } => Command::PutOpaque {
718 id: *id,
719 label: label.clone(),
720 domains: domains.clone(),
721 capabilities: *capabilities,
722 algorithm: *algorithm,
723 data: OpaqueData::try_from(data_file)?,
724 },
725 FileBackedCommand::GetOpaque { id, .. } => Command::GetOpaque { id: *id },
726 FileBackedCommand::PutWrapKey {
727 info,
728 delegated_caps,
729 passphrase_file,
730 } => Command::PutWrapKey {
731 info: info.clone(),
732 delegated_caps: delegated_caps.clone(),
733 wrapping_key: WrapKey::try_from(WrapKeyFromPassphrase::new(
734 &Passphrase::try_from(passphrase_file.as_path())?,
735 WrapKeyKind::Aes256,
736 )?)?,
737 },
738 FileBackedCommand::ExportWrapped {
739 wrap_key_id,
740 object,
741 wrapped_file: _,
742 } => Command::ExportWrapped {
743 wrap_key_id: (*wrap_key_id),
744 object: (*object),
745 },
746 FileBackedCommand::ImportWrapped {
747 wrap_key_id,
748 wrapped_file,
749 } => {
750 let message =
751 Message::from_vec(read(wrapped_file.as_path()).map_err(|source| {
752 Self::Error::IoPath {
753 path: wrapped_file.clone(),
754 context: "reading a file under wrap",
755 source,
756 }
757 })?)
758 .map_err(|source| Self::Error::InvalidWrap {
759 context: "reading the wrapped file",
760 source,
761 })?;
762
763 Command::ImportWrapped {
764 wrap_key_id: (*wrap_key_id),
765 message,
766 }
767 }
768 FileBackedCommand::DeleteObject(id) => Command::DeleteObject(*id),
769 FileBackedCommand::GetObjectInfo(id) => Command::GetObjectInfo(*id),
770 FileBackedCommand::ListObjects(filters) => Command::ListObjects(filters.clone()),
771 })
772 }
773}
774
775#[derive(Debug)]
780#[cfg(feature = "cli")]
781#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
782#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
783pub enum FileBackedCommand {
784 DeviceInfo,
786
787 ResetDeviceAndReconnect,
792
793 GetLogEntries,
795
796 SetForceAuditOption(AuditOption),
803
804 SetCommandAuditOption {
811 command: Code,
813
814 setting: AuditOption,
816 },
817
818 PutAuthenticationKey {
822 #[cfg_attr(feature = "serde", serde(flatten))]
824 info: KeyInfo,
825
826 delegated_caps: Capabilities,
829
830 passphrase_file: PathBuf,
832 },
833
834 ChangeAuthenticationKey {
838 key_id: Id,
840
841 passphrase_file: PathBuf,
843 },
844
845 GenerateAsymmetricKey {
847 #[cfg_attr(feature = "serde", serde(flatten))]
849 info: KeyInfo,
850 },
851
852 SignEd25519 {
854 key_id: Id,
856
857 data: Vec<u8>,
859 },
860
861 PutOpaque {
867 id: Id,
869
870 label: Label,
872
873 domains: Domains,
875
876 capabilities: OpaqueDataCapabilities,
878
879 algorithm: OpaqueDataAlgorithm,
881
882 data_file: OpaqueDataFile,
884 },
885
886 PutWrapKey {
891 #[cfg_attr(feature = "serde", serde(flatten))]
893 info: KeyInfo,
894
895 delegated_caps: Capabilities,
898
899 passphrase_file: PathBuf,
901 },
902
903 GetOpaque {
905 data_file: PathBuf,
907
908 id: Id,
910 },
911
912 ExportWrapped {
914 wrap_key_id: Id,
916
917 #[cfg_attr(feature = "serde", serde(flatten))]
919 object: ObjectId,
920
921 wrapped_file: PathBuf,
923 },
924
925 ImportWrapped {
927 wrap_key_id: Id,
929
930 wrapped_file: PathBuf,
932 },
933
934 DeleteObject(ObjectId),
936
937 GetObjectInfo(ObjectId),
939
940 ListObjects(Vec<ListObjectFilter>),
942}
943
944#[derive(Debug)]
949pub struct AuthenticatedCommandChain {
950 auth: Credentials,
951 commands: Vec<Command>,
952}
953
954impl AuthenticatedCommandChain {
955 pub fn new(auth: Credentials, commands: Vec<Command>) -> Self {
957 Self { auth, commands }
958 }
959
960 pub fn auth(&self) -> &Credentials {
962 &self.auth
963 }
964
965 pub fn commands(&self) -> &[Command] {
967 &self.commands
968 }
969}
970
971#[cfg(feature = "cli")]
976#[derive(Debug)]
977#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
978pub struct FileBackedAuthenticatedCommandChain {
979 pub(crate) auth: FileBackedCredentials,
980 pub(crate) commands: Vec<FileBackedCommand>,
981}
982
983#[cfg(test)]
984mod tests {
985 #[cfg(feature = "cli")]
986 use std::io::Write;
987
988 #[cfg(feature = "cli")]
989 use tempfile::{NamedTempFile, TempDir};
990 use testresult::TestResult;
991
992 use super::*;
993
994 const LARGE_DATA_LENGTH: usize = OpaqueData::MAX_DATA_SIZE + 1;
995
996 #[test]
998 fn opaque_data_new_fails_on_large_data() -> TestResult {
999 let data = Vec::from_iter([1; LARGE_DATA_LENGTH]);
1000 match OpaqueData::new(data) {
1001 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataLength {
1002 ..
1003 })) => {}
1004 Err(error) => panic!(
1005 "Expected to fail with Error::OpaqueDataLength, but got a different error instead: {error}"
1006 ),
1007 Ok(opaque_data) => panic!(
1008 "Expected to fail with Error::OpaqueDataLength, succeeded instead: {opaque_data:?}"
1009 ),
1010 };
1011
1012 Ok(())
1013 }
1014
1015 #[cfg(feature = "cli")]
1017 #[test]
1018 fn path_from_opaque_data_file() -> TestResult {
1019 let data_file = {
1020 let mut data_file = NamedTempFile::new()?;
1021 let data: Vec<u8> = Vec::from_iter([1; 1]);
1022 data_file.write_all(data.as_slice())?;
1023 data_file
1024 };
1025 let opaque_data_file = OpaqueDataFile::new(data_file.path())?;
1026 let _path: PathBuf = opaque_data_file.into();
1027
1028 Ok(())
1029 }
1030
1031 #[cfg(feature = "cli")]
1033 #[test]
1034 fn opaque_data_file_new_fails_on_dir() -> TestResult {
1035 let temp_dir = TempDir::new()?;
1036
1037 match OpaqueDataFile::new(temp_dir.path()) {
1038 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataNotAFile {
1039 ..
1040 })) => {}
1041 Err(error) => panic!(
1042 "Expected to fail with Error::OpaqueDataNotAFile, but got a different error instead: {error}"
1043 ),
1044 Ok(opaque_data) => panic!(
1045 "Expected to fail with Error::OpaqueDataNotAFile, succeeded instead: {opaque_data:?}"
1046 ),
1047 };
1048
1049 Ok(())
1050 }
1051
1052 #[cfg(feature = "cli")]
1054 #[test]
1055 fn opaque_data_file_new_fails_on_large_data() -> TestResult {
1056 let data_file = {
1057 let mut data_file = NamedTempFile::new()?;
1058 let data: Vec<u8> = Vec::from_iter([1; LARGE_DATA_LENGTH]);
1059 data_file.write_all(data.as_slice())?;
1060 data_file
1061 };
1062
1063 match OpaqueDataFile::new(data_file.path()) {
1064 Err(crate::Error::Automation(crate::automation::Error::OpaqueDataFileLength {
1065 ..
1066 })) => {}
1067 Err(error) => panic!(
1068 "Expected to fail with Error::OpaqueDataFileLength, but got a different error instead: {error}"
1069 ),
1070 Ok(opaque_data) => panic!(
1071 "Expected to fail with Error::OpaqueDataFileLength, succeeded instead: {opaque_data:?}"
1072 ),
1073 };
1074
1075 Ok(())
1076 }
1077}