use std::io::Write as _;
use std::os::unix::io::FromRawFd as _;
pub struct Passphrase(pub(crate) SecBuf<char>);
impl Passphrase {
pub fn write_stdout(&self) -> std::io::Result<()> {
let mut stdout = unsafe { std::fs::File::from_raw_fd(1) };
let mut buf: SecBuf<u8> = SecBuf::new(vec![0; 4 * self.0.len + 1]);
for c in self.0.unsecure() {
let ret = c.encode_utf8(&mut buf.buf.unsecure_mut()[buf.len..]);
buf.len += ret.len();
}
buf.buf.unsecure_mut()[buf.len] = b'\n';
buf.len += 1;
let ret = stdout.write_all(buf.unsecure());
std::mem::forget(stdout);
ret
}
}
#[derive(Debug)]
pub struct SecBuf<T: Copy + std::fmt::Debug> {
pub(crate) buf: secstr::SecVec<T>,
pub(crate) len: usize,
}
impl<T: Copy + std::fmt::Debug> SecBuf<T> {
pub fn new(buf: Vec<T>) -> Self {
Self {
buf: secstr::SecVec::new(buf),
len: 0,
}
}
pub fn unsecure(&self) -> &[T] {
&self.buf[0..self.len]
}
pub fn push(&mut self, c: T) -> bool {
let buf = self.buf.unsecure_mut();
if self.len >= buf.len() {
return false;
}
buf[self.len] = c;
self.len += 1;
true
}
pub fn insert_many<I>(&mut self, i: usize, cs: I, len: usize) -> usize
where
I: IntoIterator<Item = T>,
{
assert!(i <= self.len);
let buf = self.buf.unsecure_mut();
let len = std::cmp::min(buf.len() - self.len, len);
buf.copy_within(i..self.len, i + len);
for (k, c) in cs.into_iter().enumerate() {
if self.len >= buf.len() {
assert!(k == len);
break;
}
buf[i + k] = c;
self.len += 1;
}
len
}
pub fn delete(&mut self, start: usize, end: usize) {
assert!(end < self.len);
let buf = self.buf.unsecure_mut();
buf.copy_within(end + 1..self.len, start);
self.len -= end - start + 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secret() {
let mut buf = SecBuf::new(vec!['X'; 20]);
buf.buf.unsecure_mut()[0] = 'a';
buf.len = 1;
assert_eq!(buf.unsecure(), ['a']);
buf.len = 2;
assert_eq!(buf.unsecure(), ['a', 'X']);
}
}