use std::io::{self, Read, Write};
use rsa::{RsaPrivateKey, PublicKeyParts, RsaPublicKey, PublicKey, Oaep};
use rand::rngs::OsRng;
use sha1::Sha1;
pub struct RsaReader<'a, R: Read> {
inner: R,
key: &'a RsaPrivateKey,
cipher_block: Box<[u8]>,
clear_block: Vec<u8>,
clear_pos: usize,
}
impl<'a, R: Read> RsaReader<'a, R> {
#[inline]
pub fn new(inner: R, key: &'a RsaPrivateKey) -> Self {
Self {
inner,
key,
cipher_block: vec![0; key.size()].into_boxed_slice(),
clear_block: Vec::new(),
clear_pos: 0,
}
}
}
impl<'a, R: Read> Read for RsaReader<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.clear_pos >= self.clear_block.len() {
match self.inner.read_exact(&mut self.cipher_block) {
Ok(()) => {
self.clear_block = self.key.decrypt(Oaep::new::<Sha1>(), &self.cipher_block).unwrap();
self.clear_pos = 0;
}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
return Ok(0);
}
Err(e) => return Err(e)
}
}
let remaining = self.clear_block.len() - self.clear_pos;
let len = buf.len().min(remaining);
buf[..len].copy_from_slice(&self.clear_block[self.clear_pos..][..len]);
self.clear_pos += len;
Ok(len)
}
}
pub struct RsaWriter<'a, W: Write> {
inner: W,
key: &'a RsaPublicKey,
clear_block: Vec<u8>,
clear_block_cap: usize,
}
impl<'a, W: Write> RsaWriter<'a, W> {
pub fn new(inner: W, key: &'a RsaPublicKey) -> Self {
Self {
inner,
key,
clear_block: Vec::new(),
clear_block_cap: key.size() - 41 - 1,
}
}
}
impl<'a, W: Write> Write for RsaWriter<'a, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let remaining = self.clear_block_cap - self.clear_block.len();
let len = buf.len().min(remaining);
self.clear_block.extend_from_slice(&buf[..len]);
if remaining == len {
self.flush()?;
}
Ok(len)
}
fn flush(&mut self) -> io::Result<()> {
let len = self.clear_block.len();
if len > 0 {
let cipher_block = self.key.encrypt(&mut OsRng, Oaep::new::<Sha1>(), &self.clear_block).unwrap();
self.inner.write_all(&cipher_block)?;
self.clear_block.clear();
}
Ok(())
}
}
impl<'a, W: Write> Drop for RsaWriter<'a, W> {
fn drop(&mut self) {
let _ = Write::flush(self);
}
}