You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rsh/src/sock/enc.rs

875 lines
27 KiB

//! Socket encryption wrapper
use super::*;
use cryptohelpers::{
rsa::{
self,
RsaPublicKey,
RsaPrivateKey,
openssl::{
symm::Crypter,
error::ErrorStack,
},
},
sha256,
};
use chacha20stream::{
AsyncSink,
AsyncSource,
Key, IV,
cha,
};
use std::sync::Arc;
use tokio::{
sync::{
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
},
};
use std::{
io,
fmt,
task::{
Context, Poll,
},
pin::Pin,
marker::Unpin,
};
use smallvec::SmallVec;
/// Size of a single RSA ciphertext.
pub const RSA_CIPHERTEXT_SIZE: usize = 512;
/// A single, full block of RSA ciphertext.
type RsaCiphertextBlock = [u8; RSA_CIPHERTEXT_SIZE];
/// Max size to read when exchanging keys
const TRANS_KEY_MAX_SIZE: usize = 4096;
/// Encrypted socket information.
#[derive(Debug)]
struct ESockInfo {
us: RsaPrivateKey,
them: Option<RsaPublicKey>,
}
impl ESockInfo
{
/// Generate a new private key
pub fn new(us: impl Into<RsaPrivateKey>) -> Self
{
Self {
us: us.into(),
them: None,
}
}
/// Generate a new private key for the local endpoint
pub fn generate() -> Result<Self, rsa::Error>
{
Ok(Self::new(RsaPrivateKey::generate()?))
}
}
/// The encryption state of the Tx and Rx instances.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
struct ESockState {
encr: bool,
encw: bool,
}
impl Default for ESockState
{
#[inline]
fn default() -> Self
{
Self {
encr: false,
encw: false,
}
}
}
/// Contains a cc20 Key and IV that can be serialized and then encrypted
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct ESockSessionKey
{
key: Key,
iv: IV,
}
impl fmt::Display for ESockSessionKey
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Key: {}, IV: {}", self.key.hex(), self.iv.hex())
}
}
impl ESockSessionKey
{
/// Generate a new cc20 key + iv,
pub fn generate() -> Self
{
let (key,iv) = cha::keygen();
Self{key,iv}
}
/// Generate an encryption device
pub fn to_decrypter(&self) -> Result<Crypter, ErrorStack>
{
cha::decrypter(&self.key, &self.iv)
}
/// Generate an encryption device
pub fn to_encrypter(&self) -> Result<Crypter, ErrorStack>
{
cha::encrypter(&self.key, &self.iv)
}
/// Encrypt with RSA
pub fn to_ciphertext<K: ?Sized + rsa::PublicKey>(&self, rsa_key: &K) -> eyre::Result<RsaCiphertextBlock>
{
let mut output = [0u8; RSA_CIPHERTEXT_SIZE];
let mut temp = SmallVec::<[u8; RSA_CIPHERTEXT_SIZE]>::new(); // We know size will fit into here.
serde_cbor::to_writer(&mut temp, self)
.wrap_err(eyre!("Failed to CBOR encode session key to buffer"))
.with_section(|| self.clone().header("Session key was"))?;
debug_assert!(temp.len() < RSA_CIPHERTEXT_SIZE);
let _wr = rsa::encrypt_slice_sync(&temp, rsa_key, &mut &mut output[..])
.wrap_err(eyre!("Failed to encrypt session key with RSA public key"))
.with_section(|| self.clone().header("Session key was"))
.with_section({let temp = temp.len(); move || temp.header("Encoded data size was")})
.with_section(move || base64::encode(temp).header("Encoded data (base64) was"))?;
debug_assert_eq!(_wr, output.len());
Ok(output)
}
/// Decrypt from RSA
pub fn from_ciphertext<K: ?Sized + rsa::PrivateKey>(data: &[u8; RSA_CIPHERTEXT_SIZE], rsa_key: &K) -> eyre::Result<Self>
where <K as rsa::PublicKey>::KeyType: rsa::openssl::pkey::HasPrivate //ugh, why do we have to have this bound??? it should be implied ffs... :/
{
let mut temp = SmallVec::<[u8; RSA_CIPHERTEXT_SIZE]>::new();
rsa::decrypt_slice_sync(data, rsa_key, &mut temp)
.wrap_err(eyre!("Failed to decrypt ciphertext to session key"))
.with_section({let data = data.len(); move || data.header("Ciphertext length was")})
.with_section(|| base64::encode(data).header("Ciphertext was"))?;
Ok(serde_cbor::from_slice(&temp[..])
.wrap_err(eyre!("Failed to decode CBOR data to session key object"))
.with_section({let temp = temp.len(); move || temp.header("Encoded data size was")})
.with_section(move || base64::encode(temp).header("Encoded data (base64) was"))?)
}
}
/// A tx+rx socket.
#[pin_project]
#[derive(Debug)]
pub struct ESock<W, R> {
info: ESockInfo,
state: ESockState,
#[pin]
rx: AsyncSource<R>,
#[pin]
tx: AsyncSink<W>,
}
impl<W: AsyncWrite, R: AsyncRead> ESock<W, R>
{
fn inner(&self) -> (&W, &R)
{
(self.tx.inner(), self.rx.inner())
}
fn inner_mut(&mut self) -> (&mut W, &mut R)
{
(self.tx.inner_mut(), self.rx.inner_mut())
}
///Get a mutable ref to unencrypted read+write
fn unencrypted(&mut self) -> (&mut W, &mut R)
{
(self.tx.inner_mut(), self.rx.inner_mut())
}
/// Get a mutable ref to encrypted write+read
fn encrypted(&mut self) -> (&mut AsyncSink<W>, &mut AsyncSource<R>)
{
(&mut self.tx, &mut self.rx)
}
/// Have the RSA keys been exchanged?
pub fn has_exchanged(&self) -> bool
{
self.info.them.is_some()
}
/// Is the Write + Read operation encrypted? Tuple is `(Tx, Rx)`.
#[inline] pub fn is_encrypted(&self) -> (bool, bool)
{
(self.state.encw, self.state.encr)
}
/// Create a new `ESock` wrapper over this writer and reader with this specific RSA key.
pub fn with_key(key: impl Into<RsaPrivateKey>, tx: W, rx: R) -> Self
{
let (tk, tiv) = cha::keygen();
Self {
info: ESockInfo::new(key),
state: Default::default(),
// Note: These key+IV pairs are never used, as `state` defaults to unencrypted, and a new key/iv pair is generated when we `set_encrypted_write/read(true)`.
// TODO: Have a method to exchange these default session keys after `exchange()`?
tx: AsyncSink::encrypt(tx, tk, tiv).expect("Failed to create temp AsyncSink"),
rx: AsyncSource::encrypt(rx, tk, tiv).expect("Failed to create temp AsyncSource"),
}
}
/// Create a new `ESock` wrapper over this writer and reader with a newly generated private key
#[inline] pub fn new(tx: W, rx: R) -> Result<Self, rsa::Error>
{
Ok(Self::with_key(RsaPrivateKey::generate()?, tx, rx))
}
/// The local RSA private key
#[inline] pub fn local_key(&self) -> &RsaPrivateKey
{
&self.info.us
}
/// THe remote RSA public key (if exchange has happened.)
#[inline] pub fn foreign_key(&self) -> Option<&RsaPublicKey>
{
self.info.them.as_ref()
}
/// Split this `ESock` into a read+write pair.
///
/// # Note
/// You must preform an `exchange()` before splitting, as exchanging RSA keys is not possible on a single half.
///
/// It is also more efficient to `set_encrypted_write/read(true)` on `ESock` than it is on the halves, but changinc encryption modes on halves is still possible.
pub fn split(self) -> (ESockWriteHalf<W>, ESockReadHalf<R>)
{
let arced = Arc::new(self.info);
(ESockWriteHalf(Arc::clone(&arced), self.tx, self.state.encw),
ESockReadHalf(arced, self.rx, self.state.encr))
}
/// Merge a previously split `ESock` into a single one again.
///
/// # Panics
/// If the two halves were not split from the same `ESock`.
pub fn unsplit(txh: ESockWriteHalf<W>, rxh: ESockReadHalf<R>) -> Self
{
#[cold]
#[inline(never)]
fn _panic_ptr_ineq() -> !
{
panic!("Cannot merge halves split from different sources")
}
if !Arc::ptr_eq(&txh.0, &rxh.0) {
_panic_ptr_ineq();
}
let tx = txh.1;
drop(txh.0);
let info = Arc::try_unwrap(rxh.0).unwrap();
let rx = rxh.1;
Self {
state: ESockState {
encw: txh.2,
encr: rxh.2,
},
info,
tx, rx
}
}
}
async fn set_encrypted_write_for<T: AsyncWrite + Unpin>(info: &ESockInfo, tx: &mut AsyncSink<T>) -> eyre::Result<()>
{
use tokio::prelude::*;
let session_key = ESockSessionKey::generate();
let data = {
let them = info.them.as_ref().expect("Cannot set encrypted write when keys have not been exchanged");
session_key.to_ciphertext(them)
.wrap_err(eyre!("Failed to encrypt session key with foreign endpoint's key"))
.with_section(|| session_key.to_string().header("Session key was"))
.with_section(|| them.to_string().header("Foreign pubkey was"))?
};
let crypter = session_key.to_encrypter()
.wrap_err(eyre!("Failed to create encryption device from session key for Tx"))
.with_section(|| session_key.to_string().header("Session key was"))?;
// Send rsa `data` over unencrypted endpoint
tx.inner_mut().write_all(&data[..]).await
.wrap_err(eyre!("Failed to write ciphertext to endpoint"))
.with_section(|| data.to_base64_string().header("Ciphertext of session key was"))?;
// Set crypter of `tx` to `session_key`.
*tx.crypter_mut() = crypter;
Ok(())
}
async fn set_encrypted_read_for<T: AsyncRead + Unpin>(info: &ESockInfo, rx: &mut AsyncSource<T>) -> eyre::Result<()>
{
use tokio::prelude::*;
let mut data = [0u8; RSA_CIPHERTEXT_SIZE];
// Read `data` from unencrypted endpoint
rx.inner_mut().read_exact(&mut data[..]).await
.wrap_err(eyre!("Failed to read ciphertext from endpoint"))?;
// Decrypt `data`
let session_key = ESockSessionKey::from_ciphertext(&data, &info.us)
.wrap_err(eyre!("Failed to decrypt session key from ciphertext"))
.with_section(|| data.to_base64_string().header("Ciphertext was"))
.with_section(|| info.us.to_string().header("Our RSA key is"))?;
// Set crypter of `rx` to `session_key`.
*rx.crypter_mut() = session_key.to_decrypter()
.wrap_err(eyre!("Failed to create decryption device from session key for Rx"))
.with_section(|| session_key.to_string().header("Decrypted session key was"))?;
Ok(())
}
impl<W: AsyncWrite+ Unpin, R: AsyncRead + Unpin> ESock<W, R>
{
/// Get the Tx and Rx of the stream.
///
/// # Returns
/// Returns encrypted stream halfs if the stream is encrypted, unencrypted if not.
pub fn stream(&mut self) -> (&mut (dyn AsyncWrite + Unpin + '_), &mut (dyn AsyncRead + Unpin + '_))
{
(if self.state.encw {
&mut self.tx
} else {
self.tx.inner_mut()
}, if self.state.encr {
&mut self.rx
} else {
self.rx.inner_mut()
})
}
/// Enable write encryption
pub async fn set_encrypted_write(&mut self, set: bool) -> eyre::Result<()>
{
if set {
set_encrypted_write_for(&self.info, &mut self.tx).await?;
// Set `encw` to true
self.state.encw = true;
Ok(())
} else {
self.state.encw = false;
Ok(())
}
}
/// Enable read encryption
///
/// The other endpoint must have sent a `set_encrypted_write()`
pub async fn set_encrypted_read(&mut self, set: bool) -> eyre::Result<()>
{
if set {
set_encrypted_read_for(&self.info, &mut self.rx).await?;
// Set `encr` to true
self.state.encr = true;
Ok(())
} else {
self.state.encr = false;
Ok(())
}
}
/// Get dynamic ref to unencrypted write+read
fn unencrypted_dyn(&mut self) -> (&mut (dyn AsyncWrite + Unpin + '_), &mut (dyn AsyncRead + Unpin + '_))
{
(self.tx.inner_mut(), self.rx.inner_mut())
}
/// Get dynamic ref to encrypted write+read
fn encrypted_dyn(&mut self) -> (&mut (dyn AsyncWrite + Unpin + '_), &mut (dyn AsyncRead + Unpin + '_))
{
(&mut self.tx, &mut self.rx)
}
/// Exchange keys.
pub async fn exchange(&mut self) -> eyre::Result<()>
{
use tokio::prelude::*;
let our_key = self.info.us.get_public_parts();
let (tx, rx) = self.inner_mut();
let read_fut = {
async move {
// Read the public key from `rx`.
//TODO: Find pubkey max size.
let mut sz_buf = [0u8; std::mem::size_of::<u64>()];
rx.read_exact(&mut sz_buf[..]).await
.wrap_err(eyre!("Failed to read size of pubkey form endpoint"))?;
let sz64 = u64::from_be_bytes(sz_buf);
let sz= match usize::try_from(sz64)
.wrap_err(eyre!("Read size could not fit into u64"))
.with_section(|| format!("{:?}", sz_buf).header("Read buffer was"))
.with_section(|| u64::from_be_bytes(sz_buf).header("64=bit size value was"))
.with_warning(|| "This should not happen, it is only possible when you are running a machine with a pointer size lower than 64 bits.")
.with_suggestion(|| "The message is likely malformed. If it is not, then you are communicating with an endpoint of 64 bits whereas your pointer size is far less.")? {
x if x > TRANS_KEY_MAX_SIZE => return Err(eyre!("Recv'd key size exceeded max acceptable key buffer size")),
x => x
};
let mut key_bytes = Vec::with_capacity(sz);
tokio::io::copy(&mut rx.take(sz64), &mut key_bytes).await
.wrap_err("Failed to read key bytes into buffer")
.with_section(move || sz64.header("Pubkey size to read was"))?;
if key_bytes.len() != sz {
return Err(eyre!("Could not read required bytes"));
}
let k = RsaPublicKey::from_bytes(&key_bytes)
.wrap_err("Failed to construct RSA public key from read bytes")
.with_section(|| sz.header("Pubkey size was"))
.with_section(move || key_bytes.to_base64_string().header("Pubkey bytes were"))?;
Result::<RsaPublicKey, eyre::Report>::Ok(k)
}
};
let write_fut = {
let key_bytes = our_key.to_bytes();
assert!(key_bytes.len() <= TRANS_KEY_MAX_SIZE);
let sz64 = u64::try_from(key_bytes.len())
.wrap_err(eyre!("Size of our pubkey could not fit into u64"))
.with_section(|| key_bytes.len().header("Size was"))
.with_warning(|| "This should not happen, it is only possible when you are running a machine with a pointer size larger than 64 bits.")
.with_warning(|| "There was likely internal memory corruption.")?;
let sz_buf = sz64.to_be_bytes();
async move {
tx.write_all(&sz_buf[..]).await
.wrap_err(eyre!("Failed to write key size"))
.with_section(|| sz64.header("Key size bytes were"))
.with_section(|| format!("{:?}", sz_buf).header("Key size bytes (BE) were"))?;
tx.write_all(&key_bytes[..]).await
.wrap_err(eyre!("Failed to write key bytes"))
.with_section(|| sz64.header("Size of key was"))
.with_section(|| key_bytes.to_base64_string().header("Key bytes are"))?;
Result::<(), eyre::Report>::Ok(())
}
};
let (send, recv) = tokio::join! [write_fut, read_fut];
send.wrap_err("Failed to send our pubkey")?;
let recv = recv.wrap_err("Failed to receive foreign pubkey")?;
self.info.them = Some(recv);
Ok(())
}
}
//XXX: For some reason, non-exact reads + writes cause garbage to be produced on the receiving end?
// Is this fixable? Why does it disjoint? I have no idea... This is supposed to be a stream cipher, right? Why does positioning matter? Have I misunderstood how it workd? Eh...
// With this bug, it seems the `while read(buffer) > 0` construct is impossible. This might make this entirely useless. Hopefully with the rigid size-based format for `Message` we won't run into this problem, but subsequent data streaming will likely be affected unless we use rigid, fixed, and (inefficiently) communicated buffer sizes.
impl<W, R> AsyncWrite for ESock<W, R>
where W: AsyncWrite
{
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, io::Error>> {
//XXX: If the encryption state of the socket is changed between polls, this breaks. Idk if we can do anything about that tho.
if self.state.encw {
self.project().tx.poll_write(cx, buf)
} else {
// SAFETY: Uhh... well I think this is fine? Because we can project the container.
// TODO: Can we project the `tx`? Or maybe add a method in `AsyncSink` to map a pinned sink to a `Pin<&mut W>`?
unsafe { self.map_unchecked_mut(|this| this.tx.inner_mut()).poll_write(cx, buf)}
}
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
// Should we do anything else here?
// Should we clear foreign key/current session key?
self.project().tx.poll_shutdown(cx)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().tx.poll_flush(cx)
}
}
impl<W, R> AsyncRead for ESock<W, R>
where R: AsyncRead
{
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
//XXX: If the encryption state of the socket is changed between polls, this breaks. Idk if we can do anything about that tho.
if self.state.encr {
self.project().rx.poll_read(cx, buf)
} else {
// SAFETY: Uhh... well I think this is fine? Because we can project the container.
// TODO: Can we project the `tx`? Or maybe add a method in `AsyncSink` to map a pinned sink to a `Pin<&mut W>`?
unsafe { self.map_unchecked_mut(|this| this.rx.inner_mut()).poll_read(cx, buf)}
}
}
}
/// Write half for `ESock`.
#[pin_project]
#[derive(Debug)]
pub struct ESockWriteHalf<W>(Arc<ESockInfo>, #[pin] AsyncSink<W>, bool);
/// Read half for `ESock`.
#[pin_project]
#[derive(Debug)]
pub struct ESockReadHalf<R>(Arc<ESockInfo>, #[pin] AsyncSource<R>, bool);
//Impl AsyncRead/Write + set_encrypted_read/write for ESockRead/WriteHalf.
impl<W: AsyncWrite> ESockWriteHalf<W>
{
/// Does this write half have a live corresponding read half?
///
/// It's not required to have one, however, exchange is not possible without since it requires sticking the halves back together.
pub fn is_bidirectional(&self) -> bool
{
Arc::strong_count(&self.0) > 1
}
/// Is write encrypted on this half?
#[inline(always)] pub fn is_encrypted(&self) -> bool
{
self.2
}
/// The local RSA private key
#[inline] pub fn local_key(&self) -> &RsaPrivateKey
{
&self.0.us
}
/// THe remote RSA public key (if exchange has happened.)
#[inline] pub fn foreign_key(&self) -> Option<&RsaPublicKey>
{
self.0.them.as_ref()
}
/// End an encrypted session syncronously.
///
/// Same as calling `set_encryption(false).now_or_never()`, but more efficient.
pub fn clear_encryption(&mut self)
{
self.2 = false;
}
}
impl<R: AsyncRead> ESockReadHalf<R>
{
/// Does this read half have a live corresponding write half?
///
/// It's not required to have one, however, exchange is not possible without since it requires sticking the halves back together.
pub fn is_bidirectional(&self) -> bool
{
Arc::strong_count(&self.0) > 1
}
/// Is write encrypted on this half?
#[inline(always)] pub fn is_encrypted(&self) -> bool
{
self.2
}
/// The local RSA private key
#[inline] pub fn local_key(&self) -> &RsaPrivateKey
{
&self.0.us
}
/// THe remote RSA public key (if exchange has happened.)
#[inline] pub fn foreign_key(&self) -> Option<&RsaPublicKey>
{
self.0.them.as_ref()
}
/// End an encrypted session syncronously.
///
/// Same as calling `set_encryption(false).now_or_never()`, but more efficient.
pub fn clear_encryption(&mut self)
{
self.2 = false;
}
}
impl<W: AsyncWrite + Unpin> ESockWriteHalf<W>
{
/// Begin or end an encrypted writing session
pub async fn set_encryption(&mut self, set: bool) -> eyre::Result<()>
{
if set {
set_encrypted_write_for(&self.0, &mut self.1).await?;
self.2 = true;
} else {
self.2 = false;
}
Ok(())
}
}
impl<R: AsyncRead + Unpin> ESockReadHalf<R>
{
/// Begin or end an encrypted reading session
pub async fn set_encryption(&mut self, set: bool) -> eyre::Result<()>
{
if set {
set_encrypted_read_for(&self.0, &mut self.1).await?;
self.2 = true;
} else {
self.2 = false;
}
Ok(())
}
}
impl<W: AsyncWrite> AsyncWrite for ESockWriteHalf<W>
{
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, io::Error>> {
if self.2 {
// Encrypted
self.project().1.poll_write(cx, buf)
} else {
// Unencrypted
// SAFETY: Uhh... well I think this is fine? Because we can project the container.
// TODO: Can we project the `tx`? Or maybe add a method in `AsyncSink` to map a pinned sink to a `Pin<&mut W>`?
unsafe { self.map_unchecked_mut(|this| this.1.inner_mut()).poll_write(cx, buf)}
}
}
#[inline(always)] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().1.poll_flush(cx)
}
#[inline(always)] fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
self.project().1.poll_flush(cx)
}
}
impl<R: AsyncRead> AsyncRead for ESockReadHalf<R>
{
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<io::Result<usize>> {
if self.2 {
// Encrypted
self.project().1.poll_read(cx, buf)
} else {
// Unencrypted
// SAFETY: Uhh... well I think this is fine? Because we can project the container.
// TODO: Can we project the `tx`? Or maybe add a method in `AsyncSink` to map a pinned sink to a `Pin<&mut W>`?
unsafe { self.map_unchecked_mut(|this| this.1.inner_mut()).poll_read(cx, buf)}
}
}
}
#[cfg(test)]
mod tests
{
use super::ESock;
#[test]
fn rsa_ciphertext_len() -> crate::eyre::Result<()>
{
let data = {
use chacha20stream::cha::{KEY_SIZE, IV_SIZE};
let (key, iv) = chacha20stream::cha::keygen();
let (sz, d) = crate::bin::collect_slices_exact::<&[u8], _, {KEY_SIZE + IV_SIZE}>([key.as_ref(), iv.as_ref()]);
assert_eq!(sz, d.len());
d
};
println!("KEY+IV: {} bytes", data.len());
let key = cryptohelpers::rsa::RsaPublicKey::generate()?;
let rsa = cryptohelpers::rsa::encrypt_slice_to_vec(data, &key)?;
println!("Rsa ciphertext size: {}", rsa.len());
assert_eq!(rsa.len(), super::RSA_CIPHERTEXT_SIZE, "Incorrect RSA ciphertext length constant for cc20 KEY+IV encoding.");
Ok(())
}
#[test]
fn rsa_serial_ciphertext_len() -> crate::eyre::Result<()>
{
let data = serde_cbor::to_vec(&{
let (key, iv) = chacha20stream::cha::keygen();
super::ESockSessionKey {
key, iv,
}
}).expect("Failed to CBOR encode Key+IV");
println!("(cbor) KEY+IV: {} bytes", data.len());
let key = cryptohelpers::rsa::RsaPublicKey::generate()?;
let rsa = cryptohelpers::rsa::encrypt_slice_to_vec(data, &key)?;
println!("Rsa ciphertext size: {}", rsa.len());
assert_eq!(rsa.len(), super::RSA_CIPHERTEXT_SIZE, "Incorrect RSA ciphertext length constant for cc20 KEY+IV CBOR encoding.");
Ok(())
}
fn gen_duplex_esock(bufsz: usize) -> crate::eyre::Result<(ESock<tokio::io::DuplexStream, tokio::io::DuplexStream>, ESock<tokio::io::DuplexStream, tokio::io::DuplexStream>)>
{
use crate::*;
let (atx, brx) = tokio::io::duplex(bufsz);
let (btx, arx) = tokio::io::duplex(bufsz);
let tx = ESock::new(atx, arx).wrap_err(eyre!("Failed to create TX"))?;
let rx = ESock::new(btx, brx).wrap_err(eyre!("Failed to create RX"))?;
Ok((tx, rx))
}
#[tokio::test]
async fn esock_exchange() -> crate::eyre::Result<()>
{
use crate::*;
const VALUE: &'static [u8] = b"Hello world!";
// The duplex buffer size here is smaller than an RSA ciphertext block. So, writing the session key must be buffered with a buffer size this small (should return Pending at least once.)
// Using a low buffer size to make sure the test passes even when the entire buffer cannot be written at once.
let (mut tx, mut rx) = gen_duplex_esock(16).wrap_err(eyre!("Failed to weave socks"))?;
let writer = tokio::spawn(async move {
use tokio::prelude::*;
tx.exchange().await?;
assert!(tx.has_exchanged());
tx.set_encrypted_write(true).await?;
assert_eq!((true, false), tx.is_encrypted());
tx.write_all(VALUE).await?;
tx.write_all(VALUE).await?;
// Check resp
tx.set_encrypted_read(true).await?;
assert_eq!({
let mut chk = [0u8; 3];
tx.read_exact(&mut chk[..]).await?;
chk
}, [0xaau8,0, 0], "Failed response check");
// Write unencrypted
tx.set_encrypted_write(false).await?;
tx.write_all(&[2,1,0xfa]).await?;
Result::<_, eyre::Report>::Ok(VALUE)
});
let reader = tokio::spawn(async move {
use tokio::prelude::*;
rx.exchange().await?;
assert!(rx.has_exchanged());
rx.set_encrypted_read(true).await?;
assert_eq!((false, true), rx.is_encrypted());
let mut val = vec![0u8; VALUE.len()];
rx.read_exact(&mut val[..]).await?;
let mut val2 = vec![0u8; VALUE.len()];
rx.read_exact(&mut val2[..]).await?;
assert_eq!(val, val2);
// Send resp
rx.set_encrypted_write(true).await?;
rx.write_all(&[0xaa, 0, 0]).await?;
// Read unencrypted
rx.set_encrypted_read(false).await?;
assert_eq!({
let mut buf = [0u8; 3];
rx.read_exact(&mut buf[..]).await?;
buf
}, [2u8,1,0xfa], "2nd response incorrect");
Result::<_, eyre::Report>::Ok(val)
});
let (writer, reader) = tokio::join![writer, reader];
let writer = writer.expect("Tx task panic");
let reader = reader.expect("Rx task panic");
eprintln!("Txr: {:?}", writer);
eprintln!("Rxr: {:?}", reader);
writer?;
let val = reader?;
println!("Read: {:?}", val);
assert_eq!(&val, VALUE);
Ok(())
}
#[tokio::test]
async fn esock_split() -> crate::eyre::Result<()>
{
use super::*;
const SLICES: &'static [&'static [u8]] = &[
&[1,5,3,7,6,9,100,0],
&[7,6,2,90],
&[3,6,1,0],
&[5,1,3,3],
];
let result = SLICES.iter().map(|&slice| slice.iter().map(|&b| u64::from(b)).sum::<u64>()).sum::<u64>();
println!("Result: {}", result);
let (mut tx, mut rx) = gen_duplex_esock(super::TRANS_KEY_MAX_SIZE * 4).wrap_err(eyre!("Failed to weave socks"))?;
let (writer, reader) = {
use tokio::prelude::*;
let writer = tokio::spawn(async move {
tx.exchange().await?;
let (mut tx, mut rx) = tx.split();
//tx.set_encryption(true).await?;
let slices = &SLICES[1..];
for &slice in slices.iter()
{
println!("Writing slice: {:?}", slice);
tx.write_all(slice).await?;
}
//let mut tx = ESock::unsplit(tx, rx);
tx.write_all(SLICES[0]).await?;
Result::<_, eyre::Report>::Ok(())
});
let reader = tokio::spawn(async move {
rx.exchange().await?;
let (mut tx, mut rx) = rx.split();
//rx.set_encryption(true).await?;
let (mut mtx, mut mrx) = tokio::sync::mpsc::channel::<Vec<u8>>(16);
let sorter = tokio::spawn(async move {
let mut done = 0u64;
while let Some(buf) = mrx.recv().await
{
//buf.sort();
done += buf.iter().map(|&b| u64::from(b)).sum::<u64>();
println!("Got buffer: {:?}", buf);
tx.write_all(&buf).await?;
}
Result::<_, eyre::Report>::Ok(done)
});
let mut buffer = [0u8; 16];
while let Ok(read) = rx.read(&mut buffer[..]).await
{
if read == 0 {
break;
}
mtx.send(Vec::from(&buffer[..read])).await?;
}
drop(mtx);
let sum = sorter.await.expect("(reader) Sorter task panic")?;
Result::<_, eyre::Report>::Ok(sum)
});
let (writer, reader) = tokio::join![writer, reader];
(writer.expect("Writer task panic"),
reader.expect("Reader task panic"))
};
writer?;
assert_eq!(result, reader?);
Ok(())
}
}