1use crate::crypto;
10use crate::error::{Error, Result};
11use crate::file::open;
12use crate::ops::{check_checksums, KeyGenerator, Signer, Verifier};
13use std::borrow::Cow;
14use std::fs::File;
15use std::path::PathBuf;
16
17#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19pub enum Mode {
20 Check,
22 Generate,
24 Sign,
26 Verify,
28}
29
30#[derive(Default, Debug)]
50pub struct Signify {
51 pub mode: Option<Mode>,
53 pub comment: Option<String>,
55 pub embed: bool,
57 pub msg_file: Option<PathBuf>,
59 pub nopass: bool,
61 pub pubkey: Option<PathBuf>,
63 pub quiet: bool,
65 pub seckey: Option<PathBuf>,
67 pub sig_file: Option<PathBuf>,
69 pub gzip: bool,
71 pub key_id: Option<i32>,
73 pub args: Vec<PathBuf>,
75
76 pub pubkey_file_handle: Option<File>,
79 pub seckey_file_handle: Option<File>,
81 pub msg_file_handle: Option<File>,
83 pub sig_file_handle: Option<File>,
85 pub tty_file_handle: Option<File>,
87}
88
89impl Signify {
90 pub fn execute(&mut self) -> Result<()> {
95 match self.mode {
96 Some(Mode::Generate) => self.execute_generate(),
97 Some(Mode::Sign) => self.execute_sign(),
98 Some(Mode::Verify) => self.execute_verify(),
99 Some(Mode::Check) => self.execute_check(),
100 None => Err(Error::MissingMode),
101 }
102 }
103
104 pub fn sandbox(&self) -> Result<()> {
108 match self.mode {
109 Some(Mode::Generate) => self.sandbox_generate(),
110 Some(Mode::Sign) => self.sandbox_sign(),
111 Some(Mode::Verify) => self.sandbox_verify(),
112 Some(Mode::Check) => self.sandbox_check(),
113 None => Err(Error::MissingMode),
114 }
115 }
116
117 pub fn validate(&self) -> Result<()> {
121 match self.mode {
122 Some(Mode::Generate) => self.validate_generate(),
123 Some(Mode::Sign) => self.validate_sign(),
124 Some(Mode::Verify) => self.validate_verify(),
125 Some(Mode::Check) => self.validate_check(),
126 None => Err(Error::MissingMode),
127 }
128 }
129
130 pub fn preopen(&mut self) -> Result<()> {
132 match self.mode {
133 Some(Mode::Generate) => self.preopen_generate(),
134 Some(Mode::Sign) => self.preopen_sign(),
135 Some(Mode::Verify) => self.preopen_verify(),
136 Some(Mode::Check) => self.preopen_check(),
137 None => Err(Error::MissingMode),
138 }
139 }
140
141 fn execute_generate(&mut self) -> Result<()> {
142 let pubkey_path = self.pubkey.as_ref().ok_or(Error::RequiredArg("-p"))?;
143 let seckey_path = self.seckey.as_ref().ok_or(Error::RequiredArg("-s"))?;
144
145 let rounds = if self.nopass {
146 0
147 } else {
148 crypto::DEFAULT_ROUNDS
149 };
150 let comment = self.comment.as_deref().unwrap_or("signify");
151
152 let mut generator = KeyGenerator::new().rounds(rounds).comment(comment);
153 if let Some(id) = self.key_id {
154 generator = generator.key_id(id);
155 }
156 if let Some(tty) = self.tty_file_handle.take() {
157 generator = generator.tty_handle(tty);
158 }
159 if let (Some(pub_file), Some(sec_file)) = (
161 self.pubkey_file_handle.take(),
162 self.seckey_file_handle.take(),
163 ) {
164 generator.generate_io(pub_file, sec_file)
165 } else {
166 generator.generate(pubkey_path, seckey_path)
167 }
168 }
169
170 fn execute_sign(&mut self) -> Result<()> {
171 let seckey_path = self.seckey.as_ref().ok_or(Error::RequiredArg("-s"))?;
172 let msg_path = self.msg_file.as_ref().ok_or(Error::RequiredArg("-m"))?;
173 let sig_path = if let Some(sig_file) = self.sig_file.as_ref() {
174 Cow::Borrowed(sig_file)
175 } else {
176 let mut sig_file = msg_path.as_os_str().to_os_string();
177 sig_file.push(".sig");
178 Cow::Owned(sig_file.into())
179 };
180
181 let mut signer = Signer::new()
182 .seckey(seckey_path)
183 .embed(self.embed)
184 .gzip(self.gzip);
185 if let Some(id) = self.key_id {
186 signer = signer.key_id(id);
187 }
188 if let Some(tty) = self.tty_file_handle.take() {
189 signer = signer.tty_handle(tty);
190 }
191
192 signer.sign_io(
193 msg_path,
194 &sig_path,
195 self.msg_file_handle.take(),
196 self.sig_file_handle.take(),
197 self.seckey_file_handle.take(),
198 )
199 }
200
201 fn execute_verify(&mut self) -> Result<()> {
202 let (msg_path, sig_path) = self.resolve_verify_paths()?;
203
204 let pubkey_path = match &self.pubkey {
205 Some(p) => p,
206 None => return Err(Error::RequiredArg("-p")),
207 };
208
209 let verifier = Verifier::new()
210 .quiet(self.quiet)
211 .embed(self.embed)
212 .gzip(self.gzip)
213 .pubkey(pubkey_path);
214
215 verifier.verify_io(
216 &msg_path,
217 &sig_path,
218 self.msg_file_handle.take(),
219 self.sig_file_handle.take(),
220 self.pubkey_file_handle.take(),
221 )
222 }
223
224 fn execute_check(&mut self) -> Result<()> {
225 let pubkey_path = self.pubkey.as_ref().ok_or(Error::RequiredArg("-p"))?;
227 let sig_path = self
228 .sig_file
229 .as_ref()
230 .or_else(|| self.args.first())
231 .ok_or(Error::RequiredArg("-x"))?;
232
233 check_checksums(
234 Some(pubkey_path),
235 sig_path,
236 self.sig_file_handle.take(),
237 self.quiet,
238 )
239 }
240
241 fn sandbox_generate(&self) -> Result<()> {
242 #[cfg(target_os = "freebsd")]
243 {
244 return self.sandbox_generate_freebsd();
245 }
246
247 #[cfg(target_os = "netbsd")]
248 {
249 return self.sandbox_generate_netbsd();
250 }
251
252 #[cfg(target_os = "openbsd")]
253 {
254 return self.sandbox_generate_openbsd();
255 }
256
257 #[cfg(any(target_os = "linux", target_os = "android"))]
258 {
259 return self.sandbox_generate_linux();
260 }
261
262 #[allow(unreachable_code)]
263 Ok(())
264 }
265
266 fn sandbox_sign(&self) -> Result<()> {
267 #[cfg(target_os = "freebsd")]
268 {
269 return self.sandbox_sign_freebsd();
270 }
271
272 #[cfg(target_os = "netbsd")]
273 {
274 return self.sandbox_sign_netbsd();
275 }
276
277 #[cfg(target_os = "openbsd")]
278 {
279 return self.sandbox_sign_openbsd();
280 }
281
282 #[cfg(any(target_os = "linux", target_os = "android"))]
283 {
284 return self.sandbox_sign_linux();
285 }
286
287 #[allow(unreachable_code)]
288 Ok(())
289 }
290
291 fn sandbox_verify(&self) -> Result<()> {
292 #[cfg(target_os = "freebsd")]
293 {
294 return self.sandbox_verify_freebsd();
295 }
296
297 #[cfg(target_os = "netbsd")]
298 {
299 return self.sandbox_verify_netbsd();
300 }
301
302 #[cfg(target_os = "openbsd")]
303 {
304 return self.sandbox_verify_openbsd();
305 }
306
307 #[cfg(any(target_os = "linux", target_os = "android"))]
308 {
309 return self.sandbox_verify_linux();
310 }
311
312 #[allow(unreachable_code)]
313 Ok(())
314 }
315
316 fn sandbox_check(&self) -> Result<()> {
317 #[cfg(target_os = "freebsd")]
318 {
319 return self.sandbox_check_freebsd();
320 }
321
322 #[cfg(target_os = "netbsd")]
323 {
324 return self.sandbox_check_netbsd();
325 }
326
327 #[cfg(target_os = "openbsd")]
328 {
329 return self.sandbox_check_openbsd();
330 }
331
332 #[cfg(any(target_os = "linux", target_os = "android"))]
333 {
334 return self.sandbox_check_linux();
335 }
336
337 #[allow(unreachable_code)]
338 Ok(())
339 }
340
341 #[cfg(target_os = "freebsd")]
342 fn sandbox_generate_freebsd(&self) -> Result<()> {
343 use capsicum::{enter, CapRights, FileRights, Right};
344
345 Self::sandbox_rlimit_bsd(true)?;
347
348 let mut rights = FileRights::new();
351 rights.allow(Right::Write);
352 rights.allow(Right::Seek);
353 rights.allow(Right::Fstat);
354
355 if let Some(f) = &self.pubkey_file_handle {
356 rights.limit(f).map_err(Error::Capsicum)?;
357 }
358 if let Some(f) = &self.seckey_file_handle {
359 rights.limit(f).map_err(Error::Capsicum)?;
360 }
361
362 if let Some(f) = &self.tty_file_handle {
363 let mut tty_rights = FileRights::new();
364 tty_rights.allow(Right::Read);
365 tty_rights.allow(Right::Write);
366 tty_rights.allow(Right::Ioctl);
367 tty_rights.limit(f).map_err(Error::Capsicum)?;
368 }
369
370 enter().map_err(Error::Capsicum)?;
372
373 Ok(())
374 }
375
376 #[cfg(target_os = "freebsd")]
377 fn sandbox_sign_freebsd(&self) -> Result<()> {
378 use capsicum::{enter, CapRights, FileRights, Right};
379
380 Self::sandbox_rlimit_bsd(true)?;
382
383 let mut r_rights = FileRights::new();
385 r_rights.allow(Right::Read);
386 r_rights.allow(Right::Seek);
387 r_rights.allow(Right::Fstat);
388
389 let mut w_rights = FileRights::new();
390 w_rights.allow(Right::Write);
391 w_rights.allow(Right::Seek);
392 w_rights.allow(Right::Fstat);
393
394 if let Some(f) = &self.seckey_file_handle {
395 r_rights.limit(f).map_err(Error::Capsicum)?;
396 }
397 if let Some(f) = &self.msg_file_handle {
398 r_rights.limit(f).map_err(Error::Capsicum)?;
399 }
400
401 if let Some(f) = &self.sig_file_handle {
403 w_rights.limit(f).map_err(Error::Capsicum)?;
404 }
405
406 if let Some(f) = &self.tty_file_handle {
407 let mut tty_rights = FileRights::new();
408 tty_rights.allow(Right::Read);
409 tty_rights.allow(Right::Write);
410 tty_rights.allow(Right::Ioctl);
411 tty_rights.limit(f).map_err(Error::Capsicum)?;
412 }
413
414 enter().map_err(Error::Capsicum)?;
416
417 Ok(())
418 }
419
420 #[cfg(target_os = "freebsd")]
421 fn sandbox_verify_freebsd(&self) -> Result<()> {
422 use capsicum::{enter, CapRights, FileRights, Right};
423
424 Self::sandbox_rlimit_bsd(true)?;
426
427 let mut r_rights = FileRights::new();
429 r_rights.allow(Right::Read);
430 r_rights.allow(Right::Seek);
431 r_rights.allow(Right::Fstat);
432
433 if let Some(f) = &self.pubkey_file_handle {
434 r_rights.limit(f).map_err(Error::Capsicum)?;
435 }
436
437 if let Some(f) = &self.sig_file_handle {
440 r_rights.limit(f).map_err(Error::Capsicum)?;
441 }
442
443 if let Some(f) = &self.msg_file_handle {
444 if self.embed || self.gzip {
445 let mut w_rights = FileRights::new();
446 w_rights.allow(Right::Write);
447 w_rights.allow(Right::Seek);
448 w_rights.allow(Right::Fstat);
449 w_rights.limit(f).map_err(Error::Capsicum)?;
450 } else {
451 r_rights.limit(f).map_err(Error::Capsicum)?;
452 }
453 }
454
455 enter().map_err(Error::Capsicum)?;
457
458 Ok(())
459 }
460
461 #[cfg(target_os = "freebsd")]
462 fn sandbox_check_freebsd(&self) -> Result<()> {
463 Self::sandbox_rlimit_bsd(false)?;
465
466 Ok(())
468 }
469
470 #[cfg(target_os = "netbsd")]
471 fn sandbox_generate_netbsd(&self) -> Result<()> {
472 Self::sandbox_rlimit_bsd(true)?;
474
475 Ok(())
476 }
477
478 #[cfg(target_os = "netbsd")]
479 fn sandbox_sign_netbsd(&self) -> Result<()> {
480 Self::sandbox_rlimit_bsd(true)?;
482
483 Ok(())
484 }
485
486 #[cfg(target_os = "netbsd")]
487 fn sandbox_verify_netbsd(&self) -> Result<()> {
488 Self::sandbox_rlimit_bsd(true)?;
490
491 Ok(())
492 }
493
494 #[cfg(target_os = "netbsd")]
495 fn sandbox_check_netbsd(&self) -> Result<()> {
496 Self::sandbox_rlimit_bsd(false)?;
498
499 Ok(())
500 }
501
502 #[cfg(target_os = "openbsd")]
503 fn sandbox_generate_openbsd(&self) -> Result<()> {
504 use pledge::pledge;
505 use unveil::unveil;
506
507 Self::sandbox_rlimit_bsd(true)?;
509
510 unveil("/dev/tty", "rw")?;
512 unveil("", "")?;
513
514 pledge("stdio tty", None)?;
516
517 Ok(())
518 }
519
520 #[cfg(target_os = "openbsd")]
521 fn sandbox_sign_openbsd(&self) -> Result<()> {
522 use pledge::pledge;
523 use unveil::unveil;
524
525 Self::sandbox_rlimit_bsd(true)?;
527
528 unveil("/dev/tty", "rw")?;
530 unveil("", "")?;
531
532 pledge("stdio tty", None)?;
534
535 Ok(())
536 }
537
538 #[cfg(target_os = "openbsd")]
539 fn sandbox_verify_openbsd(&self) -> Result<()> {
540 use pledge::pledge;
541 use unveil::unveil;
542
543 Self::sandbox_rlimit_bsd(true)?;
545
546 unveil("", "")?;
548
549 pledge("stdio", None)?;
551
552 Ok(())
553 }
554
555 #[cfg(target_os = "openbsd")]
556 fn sandbox_check_openbsd(&self) -> Result<()> {
557 use pledge::pledge;
558 use unveil::unveil;
559
560 Self::sandbox_rlimit_bsd(false)?;
562
563 unveil("/", "r")?;
565 unveil("", "")?;
566
567 pledge("stdio rpath", None)?;
569
570 Ok(())
571 }
572
573 #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
574 fn sandbox_rlimit_bsd(create: bool) -> Result<()> {
575 use nix::sys::resource::{setrlimit, Resource};
576
577 const CONFINE_RLIMIT: &[Resource] = &[
581 Resource::RLIMIT_CORE,
582 Resource::RLIMIT_NPROC,
583 Resource::RLIMIT_MEMLOCK,
584 ];
585 for resource in CONFINE_RLIMIT {
586 setrlimit(*resource, 0, 0)?;
587 }
588 if !create {
589 setrlimit(Resource::RLIMIT_FSIZE, 0, 0)?;
590 }
591
592 Ok(())
593 }
594
595 #[cfg(any(target_os = "linux", target_os = "android"))]
596 fn sandbox_basic_linux() -> Result<()> {
597 use nix::sys::prctl::{set_dumpable, set_no_new_privs};
598 use capctl::{set_mdwe, MDWEFlags};
600
601 set_no_new_privs()?;
603
604 set_dumpable(false)?;
606
607 let _ = set_mdwe(MDWEFlags::REFUSE_EXEC_GAIN);
609
610 Ok(())
611 }
612
613 #[cfg(any(target_os = "linux", target_os = "android"))]
614 fn sandbox_rlimit_linux(create: bool) -> Result<()> {
615 use nix::sys::resource::{setrlimit, Resource};
616
617 const CONFINE_RLIMIT: &[Resource] = &[
621 Resource::RLIMIT_CORE,
622 Resource::RLIMIT_NPROC,
623 Resource::RLIMIT_LOCKS,
624 Resource::RLIMIT_MEMLOCK,
625 Resource::RLIMIT_MSGQUEUE,
626 ];
627 for resource in CONFINE_RLIMIT {
628 setrlimit(*resource, 0, 0)?;
629 }
630 if !create {
631 setrlimit(Resource::RLIMIT_FSIZE, 0, 0)?;
632 }
633
634 Ok(())
635 }
636
637 #[cfg(any(target_os = "linux", target_os = "android"))]
638 fn sandbox_generate_linux(&self) -> Result<()> {
639 Self::sandbox_basic_linux()?;
641
642 Self::sandbox_rlimit_linux(true)?;
644
645 let _ = Self::sandbox_generate_landlock();
647
648 Ok(())
649 }
650
651 #[cfg(any(target_os = "linux", target_os = "android"))]
652 fn sandbox_sign_linux(&self) -> Result<()> {
653 Self::sandbox_basic_linux()?;
655
656 Self::sandbox_rlimit_linux(true)?;
658
659 let _ = Self::sandbox_sign_landlock();
661
662 Ok(())
663 }
664
665 #[cfg(any(target_os = "linux", target_os = "android"))]
666 fn sandbox_verify_linux(&self) -> Result<()> {
667 Self::sandbox_basic_linux()?;
669
670 Self::sandbox_rlimit_linux(true)?;
672
673 let _ = Self::sandbox_verify_landlock();
675
676 Ok(())
677 }
678
679 #[cfg(any(target_os = "linux", target_os = "android"))]
680 fn sandbox_check_linux(&self) -> Result<()> {
681 Self::sandbox_basic_linux()?;
683
684 Self::sandbox_rlimit_linux(false)?;
686
687 let _ = Self::sandbox_check_landlock();
689
690 Ok(())
691 }
692
693 #[cfg(any(target_os = "linux", target_os = "android"))]
694 fn sandbox_generate_landlock() -> Result<()> {
695 use landlock::{AccessFs, BitFlags, RulesetCreatedAttr};
696
697 let mut ruleset = Self::sandbox_init_landlock()?;
699
700 let confine_path: &[(&str, BitFlags<AccessFs, u64>)] = &[
703 (
704 "/dev/tty",
705 AccessFs::ReadFile | AccessFs::WriteFile | AccessFs::IoctlDev,
706 ),
707 ("/dev/random", AccessFs::ReadFile.into()),
708 ("/dev/urandom", AccessFs::ReadFile.into()),
709 ];
710 for (path, access_fs) in confine_path {
711 ruleset = ruleset.add_rules(landlock::path_beneath_rules(&[path], *access_fs))?;
712 }
713
714 ruleset.restrict_self()?;
715
716 Ok(())
717 }
718
719 #[cfg(any(target_os = "linux", target_os = "android"))]
720 fn sandbox_sign_landlock() -> Result<()> {
721 Self::sandbox_generate_landlock()
722 }
723
724 #[cfg(any(target_os = "linux", target_os = "android"))]
725 fn sandbox_verify_landlock() -> Result<()> {
726 Self::sandbox_init_landlock()?.restrict_self()?;
728
729 Ok(())
730 }
731
732 #[cfg(any(target_os = "linux", target_os = "android"))]
733 fn sandbox_check_landlock() -> Result<()> {
734 use landlock::{AccessFs, RulesetCreatedAttr};
735
736 let ruleset = Self::sandbox_init_landlock()?;
738
739 ruleset
741 .add_rules(landlock::path_beneath_rules(&["/"], AccessFs::ReadFile))?
742 .restrict_self()?;
743
744 Ok(())
745 }
746
747 #[cfg(any(target_os = "linux", target_os = "android"))]
748 fn sandbox_init_landlock() -> Result<landlock::RulesetCreated> {
749 use landlock::{
750 Access, AccessFs, AccessNet, CompatLevel, Compatible, Ruleset, RulesetAttr, Scope, ABI,
751 };
752
753 Ok(Ruleset::default()
754 .handle_access(AccessFs::from_all(ABI::V6))?
755 .handle_access(AccessNet::from_all(ABI::V6))?
756 .scope(Scope::from_all(ABI::V6))?
757 .set_compatibility(CompatLevel::BestEffort)
758 .create()?)
759 }
760
761 fn preopen_tty(&mut self) -> Result<()> {
762 #[cfg(unix)]
763 if !self.nopass {
764 use std::os::unix::fs::OpenOptionsExt;
765
766 self.tty_file_handle = std::fs::OpenOptions::new()
769 .read(true)
770 .write(true)
771 .custom_flags(nix::libc::O_NOFOLLOW)
772 .open("/dev/tty")
773 .ok();
774 }
775 Ok(())
776 }
777
778 fn preopen_generate(&mut self) -> Result<()> {
779 self.preopen_tty()?;
780
781 let pubkey_path = self.pubkey.as_ref().ok_or(Error::RequiredArg("-p"))?;
782 let seckey_path = self.seckey.as_ref().ok_or(Error::RequiredArg("-s"))?;
783
784 self.pubkey_file_handle = Some(open(pubkey_path, true)?);
785 self.seckey_file_handle = Some(open(seckey_path, true)?);
786
787 Ok(())
788 }
789
790 fn preopen_sign(&mut self) -> Result<()> {
791 self.preopen_tty()?;
792
793 if let Some(path) = &self.seckey {
794 if path.as_os_str() != "-" {
795 self.seckey_file_handle = Some(open(path, false)?);
796 }
797 }
798
799 if let Some(path) = &self.msg_file {
800 if path.as_os_str() != "-" {
801 self.msg_file_handle = Some(open(path, false)?);
802 }
803 }
804
805 let sig_path = self.sig_file.clone().unwrap_or_else(|| {
806 if let Some(msg) = &self.msg_file {
807 let mut sig_name = msg.as_os_str().to_os_string();
808 sig_name.push(".sig");
809 PathBuf::from(sig_name)
810 } else {
811 PathBuf::from("-")
812 }
813 });
814
815 if sig_path.as_os_str() != "-" {
816 self.sig_file_handle = Some(open(&sig_path, true)?);
817 }
818
819 Ok(())
820 }
821
822 fn preopen_verify(&mut self) -> Result<()> {
823 if let Some(path) = &self.pubkey {
824 self.pubkey_file_handle = Some(open(path, false)?);
825 }
826
827 let (msg_path, sig_path) = self.resolve_verify_paths()?;
828 if msg_path.as_os_str() != "-" {
829 if self.embed || self.gzip {
832 self.msg_file_handle = Some(open(&msg_path, true)?);
833 } else {
834 self.msg_file_handle = Some(open(&msg_path, false)?);
835 }
836 }
837
838 if sig_path.as_os_str() != "-" {
839 self.sig_file_handle = Some(open(&sig_path, false)?);
840 }
841
842 Ok(())
843 }
844
845 fn preopen_check(&mut self) -> Result<()> {
846 if let Some(path) = &self.pubkey {
847 self.pubkey_file_handle = Some(open(path, false)?);
848 }
849
850 let sig_path = self
851 .sig_file
852 .clone()
853 .or_else(|| self.args.first().cloned())
854 .unwrap_or_else(|| PathBuf::from("-"));
855 if sig_path.as_os_str() != "-" {
856 self.sig_file_handle = Some(open(&sig_path, false)?);
857 }
858 Ok(())
859 }
860
861 fn resolve_verify_paths(&self) -> Result<(PathBuf, PathBuf)> {
862 if self.gzip {
863 let sig = self
864 .sig_file
865 .clone()
866 .or_else(|| self.args.first().cloned())
867 .unwrap_or_else(|| PathBuf::from("-"));
868 let msg = self.msg_file.clone().unwrap_or_else(|| PathBuf::from("-"));
869 Ok((msg, sig))
870 } else {
871 let msg = self.msg_file.clone().ok_or(Error::RequiredArg("-m"))?;
872 let sig = self
873 .sig_file
874 .clone()
875 .or_else(|| self.args.first().cloned())
876 .unwrap_or_else(|| {
877 let mut sig_name = msg.as_os_str().to_os_string();
878 sig_name.push(".sig");
879 PathBuf::from(sig_name)
880 });
881 Ok((msg, sig))
882 }
883 }
884
885 fn validate_generate(&self) -> Result<()> {
886 if self.pubkey.is_none() {
887 return Err(Error::RequiredArg("-p"));
888 }
889 if self.seckey.is_none() {
890 return Err(Error::RequiredArg("-s"));
891 }
892 Ok(())
893 }
894
895 fn validate_sign(&self) -> Result<()> {
896 if self.seckey.is_none() {
897 return Err(Error::RequiredArg("-s"));
898 }
899 if self.msg_file.is_none() {
900 return Err(Error::RequiredArg("-m"));
901 }
902 if self.gzip && self.sig_file.is_none() {
903 return Err(Error::RequiredArg("-x"));
904 }
905 if self.sig_file.is_none() {
906 if let Some(msg) = &self.msg_file {
907 if msg.as_os_str() == "-" {
908 return Err(Error::RequiredArg("-x"));
909 }
910 }
911 }
912 Ok(())
913 }
914
915 fn validate_verify(&self) -> Result<()> {
916 if self.pubkey.is_none() {
917 return Err(Error::RequiredArg("-p"));
918 }
919 if !self.gzip && self.msg_file.is_none() {
920 return Err(Error::RequiredArg("-m"));
921 }
922 Ok(())
923 }
924
925 fn validate_check(&self) -> Result<()> {
926 if self.pubkey.is_none() {
927 return Err(Error::RequiredArg("-p"));
928 }
929 if self.sig_file.is_none() && self.args.is_empty() {
930 return Err(Error::RequiredArg("-x"));
931 }
932 Ok(())
933 }
934}