age/cli_common/
recipients.rs1use std::io::{self, BufReader};
2
3use super::StdinGuard;
4use super::{identities::parse_identity_files, ReadError};
5use crate::identity::RecipientsAccumulator;
6use crate::{x25519, Recipient};
7
8#[cfg(feature = "plugin")]
9use crate::{cli_common::UiCallbacks, plugin};
10
11#[cfg(not(feature = "plugin"))]
12use std::convert::Infallible;
13
14#[cfg(feature = "ssh")]
15use crate::ssh;
16
17#[cfg(any(feature = "armor", feature = "plugin"))]
18use crate::EncryptError;
19
20#[cfg(feature = "ssh")]
26fn parse_ssh_recipient<F, G>(
27 parser: F,
28 invalid: G,
29 filename: &str,
30) -> Result<Option<Box<dyn Recipient + Send>>, ReadError>
31where
32 F: FnOnce() -> Result<ssh::Recipient, ssh::ParseRecipientKeyError>,
33 G: FnOnce() -> Result<Option<Box<dyn Recipient + Send>>, ReadError>,
34{
35 use ssh::{ParseRecipientKeyError, UnsupportedKey};
36
37 match parser() {
38 Ok(pk) => Ok(Some(Box::new(pk))),
39 Err(e) => match e {
40 ParseRecipientKeyError::Ignore => Ok(None),
41 ParseRecipientKeyError::Invalid(_) => invalid(),
42 ParseRecipientKeyError::RsaModulusTooLarge => Err(ReadError::RsaModulusTooLarge),
43 ParseRecipientKeyError::RsaModulusTooSmall => Err(ReadError::RsaModulusTooSmall),
44 ParseRecipientKeyError::Unsupported(key_type) => Err(ReadError::UnsupportedKey(
45 filename.to_string(),
46 UnsupportedKey::from_key_type(key_type),
47 )),
48 },
49 }
50}
51
52fn parse_recipient(
54 _filename: &str,
55 s: String,
56 recipients: &mut RecipientsAccumulator,
57) -> Result<(), ReadError> {
58 if let Ok(pk) = s.parse::<x25519::Recipient>() {
59 recipients.push(Box::new(pk));
60 } else if let Some(pk) = {
61 #[cfg(feature = "ssh")]
62 {
63 parse_ssh_recipient(|| s.parse::<ssh::Recipient>(), || Ok(None), _filename)?
64 }
65
66 #[cfg(not(feature = "ssh"))]
67 None
68 } {
69 recipients.push(pk);
70 } else if let Some(_recipient) = {
71 #[cfg(feature = "plugin")]
72 {
73 s.parse::<plugin::Recipient>().ok()
75 }
76
77 #[cfg(not(feature = "plugin"))]
78 None::<Infallible>
79 } {
80 #[cfg(feature = "plugin")]
81 recipients.push_plugin(_recipient);
82 } else {
83 return Err(ReadError::InvalidRecipient(s));
84 }
85
86 Ok(())
87}
88
89fn read_recipients_list<R: io::BufRead>(
91 filename: &str,
92 buf: R,
93 recipients: &mut RecipientsAccumulator,
94) -> Result<(), ReadError> {
95 for (line_number, line) in buf.lines().enumerate() {
96 let line = line?;
97
98 if line.is_empty() || line.find('#') == Some(0) {
100 continue;
101 } else if let Err(_e) = parse_recipient(filename, line, recipients) {
102 #[cfg(feature = "ssh")]
103 match _e {
104 ReadError::RsaModulusTooLarge
105 | ReadError::RsaModulusTooSmall
106 | ReadError::UnsupportedKey(_, _) => {
107 return Err(io::Error::new(io::ErrorKind::InvalidData, _e.to_string()).into());
108 }
109 _ => (),
110 }
111
112 return Err(ReadError::InvalidRecipientsFile {
115 filename: filename.to_owned(),
116 line_number: line_number + 1,
117 });
118 }
119 }
120
121 Ok(())
122}
123
124pub fn read_recipients(
130 recipient_strings: Vec<String>,
131 recipients_file_strings: Vec<String>,
132 identity_strings: Vec<String>,
133 max_work_factor: Option<u8>,
134 stdin_guard: &mut StdinGuard,
135) -> Result<Vec<Box<dyn Recipient + Send>>, ReadError> {
136 let mut recipients = RecipientsAccumulator::new();
137
138 for arg in recipient_strings {
139 parse_recipient("", arg, &mut recipients)?;
140 }
141
142 for arg in recipients_file_strings {
143 let f = stdin_guard.open(arg.clone()).map_err(|e| match e {
144 ReadError::Io(e) if matches!(e.kind(), io::ErrorKind::NotFound) => {
145 ReadError::MissingRecipientsFile(arg.clone())
146 }
147 _ => e,
148 })?;
149 let buf = BufReader::new(f);
150 read_recipients_list(&arg, buf, &mut recipients)?;
151 }
152
153 parse_identity_files::<_, ReadError>(
154 identity_strings,
155 max_work_factor,
156 stdin_guard,
157 &mut recipients,
158 #[cfg(feature = "armor")]
159 |recipients, identity| {
160 recipients.extend(identity.recipients().map_err(|e| {
161 if let EncryptError::EncryptedIdentities(e) = e {
163 ReadError::EncryptedIdentities(e)
164 } else {
165 unreachable!()
166 }
167 })?);
168 Ok(())
169 },
170 #[cfg(feature = "ssh")]
171 |recipients, filename, identity| {
172 let recipient = parse_ssh_recipient(
173 || ssh::Recipient::try_from(identity),
174 || Err(ReadError::InvalidRecipient(filename.to_owned())),
175 filename,
176 )?
177 .expect("unsupported identities were already handled");
178 recipients.push(recipient);
179 Ok(())
180 },
181 |recipients, identity_file| {
182 recipients.with_identities(identity_file);
183 Ok(())
184 },
185 )?;
186
187 recipients
188 .build(
189 #[cfg(feature = "plugin")]
190 UiCallbacks,
191 )
192 .map_err(|_e| {
193 #[cfg(feature = "plugin")]
195 {
196 if let EncryptError::MissingPlugin { binary_name } = _e {
197 ReadError::MissingPlugin { binary_name }
198 } else {
199 unreachable!()
200 }
201 }
202
203 #[cfg(not(feature = "plugin"))]
204 unreachable!()
205 })
206}