Improved error messages for `set_encrypted_read()` and `exchange()`.

Fortune for rsh's current commit: Future small blessing − 末小吉
master
Avril 3 years ago
parent 087a076b40
commit b6b3bb0fd5
Signed by: flanchan
GPG Key ID: 284488987C31F630

5
Cargo.lock generated

@ -185,10 +185,11 @@ dependencies = [
[[package]] [[package]]
name = "cryptohelpers" name = "cryptohelpers"
version = "1.8.1" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14be74ce15793a86acd04872953368ce27d07f384f07b8028bd5aaa31a031a38" checksum = "9143447fb393f8d38abbb617af9b986a0941785ddc63685bd8de735fb31bcafc"
dependencies = [ dependencies = [
"base64 0.13.0",
"crc", "crc",
"futures", "futures",
"getrandom 0.1.16", "getrandom 0.1.16",

@ -11,7 +11,7 @@ base64 = "0.13.0"
bytes = { version = "1.0.1", features = ["serde"] } bytes = { version = "1.0.1", features = ["serde"] }
chacha20stream = { version = "2.1.0", features = ["async", "serde"] } chacha20stream = { version = "2.1.0", features = ["async", "serde"] }
color-eyre = "0.5.11" color-eyre = "0.5.11"
cryptohelpers = { version = "1.8.1" , features = ["serialise", "full"] } cryptohelpers = { version = "1.8.2" , features = ["serialise", "full"] }
futures = "0.3.16" futures = "0.3.16"
mopa = "0.2.2" mopa = "0.2.2"
pin-project = "1.0.8" pin-project = "1.0.8"

@ -0,0 +1,15 @@
//! Base64 formatting extensions
use super::*;
pub trait Base64StringExt
{
fn to_base64_string(&self) -> String;
}
impl<T: ?Sized> Base64StringExt for T
where T: AsRef<[u8]>
{
fn to_base64_string(&self) -> String {
::base64::encode(self.as_ref())
}
}

@ -10,6 +10,9 @@ pub use alloc::*;
mod hex; mod hex;
pub use hex::*; pub use hex::*;
mod base64;
pub use self::base64::*;
/// A maybe-atom that can spill into a vector. /// A maybe-atom that can spill into a vector.
pub type MaybeVec<T> = SmallVec<[T; 1]>; pub type MaybeVec<T> = SmallVec<[T; 1]>;

@ -56,12 +56,44 @@ struct ESockInfo {
them: Option<RsaPublicKey>, them: Option<RsaPublicKey>,
} }
#[derive(Debug)] 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 { struct ESockState {
encr: bool, encr: bool,
encw: 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 /// Contains a cc20 Key and IV that can be serialized and then encrypted
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct ESockSessionKey struct ESockSessionKey
@ -188,16 +220,42 @@ impl<W: AsyncWrite, R: AsyncRead> ESock<W, R>
impl<W: AsyncWrite+ Unpin, R: AsyncRead + Unpin> ESock<W, R> 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 /// Enable write encryption
pub async fn set_encrypted_write(&mut self, set: bool) -> eyre::Result<()> pub async fn set_encrypted_write(&mut self, set: bool) -> eyre::Result<()>
{ {
use tokio::prelude::*; use tokio::prelude::*;
if set { if set {
let session_key = ESockSessionKey::generate(); let session_key = ESockSessionKey::generate();
let data = session_key.to_ciphertext(self.info.them.as_ref().expect("Cannot set encrypted write when keys have not been exchanged"))?; let data = {
let crypter = session_key.to_encrypter()?; let them = self.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 // Send rsa `data` over unencrypted endpoint
self.unencrypted().0.write_all(&data[..]).await?; self.unencrypted().0.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`. // Set crypter of `tx` to `session_key`.
*self.tx.crypter_mut() = crypter; *self.tx.crypter_mut() = crypter;
// Set `encw` to true // Set `encw` to true
@ -218,11 +276,17 @@ impl<W: AsyncWrite+ Unpin, R: AsyncRead + Unpin> ESock<W, R>
if set { if set {
let mut data = [0u8; RSA_CIPHERTEXT_SIZE]; let mut data = [0u8; RSA_CIPHERTEXT_SIZE];
// Read `data` from unencrypted endpoint // Read `data` from unencrypted endpoint
self.unencrypted().1.read_exact(&mut data[..]).await?; self.unencrypted().1.read_exact(&mut data[..]).await
.wrap_err(eyre!("Failed to read ciphertext from endpoint"))?;
// Decrypt `data` // Decrypt `data`
let session_key = ESockSessionKey::from_ciphertext(&data, &self.info.us)?; let session_key = ESockSessionKey::from_ciphertext(&data, &self.info.us)
.wrap_err(eyre!("Failed to decrypt session key from ciphertext"))
.with_section(|| data.to_base64_string().header("Ciphertext was"))
.with_section(|| self.info.us.to_string().header("Our RSA key is"))?;
// Set crypter of `rx` to `session_key`. // Set crypter of `rx` to `session_key`.
*self.rx.crypter_mut() = session_key.to_decrypter()?; *self.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"))?;
// Set `encr` to true // Set `encr` to true
self.state.encr = true; self.state.encr = true;
Ok(()) Ok(())
@ -255,17 +319,29 @@ impl<W: AsyncWrite+ Unpin, R: AsyncRead + Unpin> ESock<W, R>
// Read the public key from `rx`. // Read the public key from `rx`.
//TODO: Find pubkey max size. //TODO: Find pubkey max size.
let mut sz_buf = [0u8; std::mem::size_of::<u64>()]; let mut sz_buf = [0u8; std::mem::size_of::<u64>()];
rx.read_exact(&mut sz_buf[..]).await?; rx.read_exact(&mut sz_buf[..]).await
let sz= match usize::try_from(u64::from_be_bytes(sz_buf))? { .wrap_err(eyre!("Failed to read size of pubkey form endpoint"))?;
x if x > TRANS_KEY_MAX_SIZE => return Err(eyre!("Recv'd key size exceeded max")), let sz64 = u64::from_be_bytes(sz_buf);
x => x 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); let mut key_bytes = Vec::with_capacity(sz);
tokio::io::copy(&mut rx.take(sz as u64), &mut key_bytes).await?; 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 { if key_bytes.len() != sz {
return Err(eyre!("Could not read required bytes")); return Err(eyre!("Could not read required bytes"));
} }
let k = RsaPublicKey::from_bytes(key_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) Result::<RsaPublicKey, eyre::Report>::Ok(k)
} }
@ -273,16 +349,27 @@ impl<W: AsyncWrite+ Unpin, R: AsyncRead + Unpin> ESock<W, R>
let write_fut = { let write_fut = {
let key_bytes = our_key.to_bytes(); let key_bytes = our_key.to_bytes();
assert!(key_bytes.len() <= TRANS_KEY_MAX_SIZE); assert!(key_bytes.len() <= TRANS_KEY_MAX_SIZE);
let sz_buf = u64::try_from(key_bytes.len())?.to_be_bytes(); 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 { async move {
tx.write_all(&sz_buf[..]).await?; tx.write_all(&sz_buf[..]).await
tx.write_all(&key_bytes[..]).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(()) Result::<(), eyre::Report>::Ok(())
} }
}; };
let (send, recv) = tokio::join! [write_fut, read_fut]; let (send, recv) = tokio::join! [write_fut, read_fut];
send?; send.wrap_err("Failed to send our pubkey")?;
let recv = recv?; let recv = recv.wrap_err("Failed to receive foreign pubkey")?;
self.info.them = Some(recv); self.info.them = Some(recv);
Ok(()) Ok(())
} }

Loading…
Cancel
Save