fedimint_core/util/
secret_file.rs1use std::io::Read;
4use std::path::Path;
5
6#[derive(Debug, thiserror::Error)]
8pub enum SecretInputError {
9 #[error("Could not open secret input file")]
11 Open,
12 #[error("Could not read secret input")]
14 Read,
15 #[error("Invalid secret input size limit")]
17 InvalidLimit,
18 #[error("Secret input exceeds size limit")]
20 TooLarge,
21 #[error("Secret input is not valid UTF-8")]
23 InvalidUtf8,
24 #[error("Only one secret input may read standard input")]
26 MultipleStdin,
27}
28
29pub fn read_secret_file(path: &Path, max_bytes: usize) -> Result<String, SecretInputError> {
40 if path == Path::new("-") {
41 read_secret(std::io::stdin().lock(), max_bytes)
42 } else {
43 let file = std::fs::File::open(path).map_err(|_| SecretInputError::Open)?;
44 read_secret(file, max_bytes)
45 }
46}
47
48pub fn ensure_single_stdin<'a>(
50 paths: impl IntoIterator<Item = &'a Path>,
51) -> Result<(), SecretInputError> {
52 if paths
53 .into_iter()
54 .filter(|path| *path == Path::new("-"))
55 .take(2)
56 .count()
57 > 1
58 {
59 return Err(SecretInputError::MultipleStdin);
60 }
61 Ok(())
62}
63
64fn read_secret(reader: impl Read, max_bytes: usize) -> Result<String, SecretInputError> {
65 let limit = u64::try_from(max_bytes)
66 .ok()
67 .and_then(|limit| limit.checked_add(1))
68 .ok_or(SecretInputError::InvalidLimit)?;
69 let mut bytes = Vec::new();
70 reader
71 .take(limit)
72 .read_to_end(&mut bytes)
73 .map_err(|_| SecretInputError::Read)?;
74 if bytes.len() > max_bytes {
75 return Err(SecretInputError::TooLarge);
76 }
77 let mut value = String::from_utf8(bytes).map_err(|_| SecretInputError::InvalidUtf8)?;
78 if value.ends_with('\n') {
79 value.pop();
80 if value.ends_with('\r') {
81 value.pop();
82 }
83 }
84 Ok(value)
85}
86
87#[cfg(test)]
88mod tests;