1use std::{
10 ffi::{CStr, FromBytesUntilNulError},
11 io, mem,
12 str::Utf8Error,
13};
14
15use bitflags::bitflags;
16use thiserror::Error;
17#[cfg(feature = "zeroize")]
18use zeroize::Zeroizing;
19
20const PASSWORD_LEN: usize = 256;
21
22bitflags! {
23 pub struct RppFlags: i32 {
25 const ECHO_OFF = 0x00;
27 const ECHO_ON = 0x01;
29 const REQUIRE_TTY = 0x02;
31 const FORCELOWER = 0x04;
33 const FORCEUPPER = 0x08;
35 const SEVENBIT = 0x10;
37 const STDIN = 0x20;
39 }
40}
41
42impl Default for RppFlags {
43 fn default() -> Self {
44 Self::ECHO_OFF
45 }
46}
47
48#[derive(Error, Debug)]
49pub enum Error {
50 #[error(transparent)]
51 IoError(#[from] io::Error),
52 #[error(transparent)]
53 Utf8Error(#[from] Utf8Error),
54 #[error(transparent)]
55 CStrError(#[from] FromBytesUntilNulError),
56}
57
58pub fn readpassphrase(prompt: &CStr, flags: RppFlags) -> Result<String, Error> {
62 readpassphrase_buf(prompt, vec![0u8; PASSWORD_LEN], flags)
63}
64
65pub fn readpassphrase_buf(
69 prompt: &CStr,
70 #[allow(unused_mut)] mut buf: Vec<u8>,
71 flags: RppFlags,
72) -> Result<String, Error> {
73 #[cfg(feature = "zeroize")]
74 let mut buf = Zeroizing::new(buf);
75 unsafe {
76 let res = ffi::readpassphrase(
77 prompt.as_ptr(),
78 buf.as_mut_ptr().cast(),
79 buf.len(),
80 flags.bits(),
81 );
82 if res.is_null() {
83 return Err(io::Error::last_os_error().into());
84 }
85 }
86 let nul_pos = buf
87 .iter()
88 .position(|&b| b == 0)
89 .ok_or(io::Error::from(io::ErrorKind::InvalidData))?;
90 buf.truncate(nul_pos);
91 let _ = str::from_utf8(&buf)?;
92 Ok(unsafe { String::from_utf8_unchecked(mem::take(&mut buf)) })
93}
94
95pub fn readpassphrase_inplace<'a>(
99 prompt: &CStr,
100 buf: &'a mut [u8],
101 flags: RppFlags,
102) -> Result<&'a str, Error> {
103 unsafe {
104 let res = ffi::readpassphrase(
105 prompt.as_ptr(),
106 buf.as_mut_ptr().cast(),
107 buf.len(),
108 flags.bits(),
109 );
110 if res.is_null() {
111 return Err(io::Error::last_os_error().into());
112 }
113 }
114 let res = CStr::from_bytes_until_nul(buf)?;
115 Ok(res.to_str()?)
116}
117
118mod ffi {
119 use std::ffi::{c_char, c_int};
120
121 unsafe extern "C" {
122 pub(crate) unsafe fn readpassphrase(
123 prompt: *const c_char,
124 buf: *mut c_char,
125 bufsiz: usize,
126 flags: c_int,
127 ) -> *mut c_char;
128 }
129}