mirror of https://github.com/Trivernis/bromine.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
6.2 KiB
Rust
180 lines
6.2 KiB
Rust
use crate::prelude::encrypted::{EncryptedStream, Keys};
|
|
use crate::prelude::{IPCError, IPCResult};
|
|
use crate::protocol::AsyncProtocolStream;
|
|
use bytes::Bytes;
|
|
use chacha20poly1305::aead::{Aead, NewAead};
|
|
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
|
use rand::thread_rng;
|
|
use rand_core::RngCore;
|
|
use sha2::{Digest, Sha256};
|
|
use std::io;
|
|
use std::io::ErrorKind;
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
use std::sync::Arc;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use x25519_dalek::{PublicKey, StaticSecret};
|
|
|
|
/// A structure used for encryption.
|
|
/// It holds the cipher initialized with the given key
|
|
/// and two counters for both encryption and decryption
|
|
/// count which are used to keep track of the nonce.
|
|
#[derive(Clone)]
|
|
pub(crate) struct CipherBox {
|
|
cipher: ChaCha20Poly1305,
|
|
en_count: Arc<AtomicU64>,
|
|
de_count: Arc<AtomicU64>,
|
|
}
|
|
|
|
impl CipherBox {
|
|
pub fn new(key: Bytes) -> Self {
|
|
let key = Key::from_slice(&key[..]);
|
|
let cipher = ChaCha20Poly1305::new(key);
|
|
|
|
Self {
|
|
cipher,
|
|
en_count: Arc::new(AtomicU64::new(0)),
|
|
de_count: Arc::new(AtomicU64::new(0)),
|
|
}
|
|
}
|
|
|
|
/// Encrypts the given message
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
|
pub fn encrypt(&self, data: Bytes) -> io::Result<Bytes> {
|
|
self.cipher
|
|
.encrypt(&self.en_nonce(), &data[..])
|
|
.map(Bytes::from)
|
|
.map_err(|_| io::Error::from(ErrorKind::InvalidData))
|
|
}
|
|
|
|
/// Decrypts the given message
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
|
pub fn decrypt(&self, data: Bytes) -> io::Result<Bytes> {
|
|
self.cipher
|
|
.decrypt(&self.de_nonce(), &data[..])
|
|
.map(Bytes::from)
|
|
.map_err(|_| io::Error::from(ErrorKind::InvalidData))
|
|
}
|
|
|
|
/// Updates the stored key.
|
|
/// This must be done simultaneously on server and client side
|
|
/// to keep track of the nonce
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
|
pub fn update_key(&mut self, key: Bytes) {
|
|
let key = Key::from_slice(&key[..]);
|
|
self.cipher = ChaCha20Poly1305::new(key);
|
|
self.reset_counters();
|
|
}
|
|
|
|
/// Resets the nonce counters.
|
|
/// This must be done simultaneously on server and client side.
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
|
pub fn reset_counters(&mut self) {
|
|
self.de_count.store(0, Ordering::SeqCst);
|
|
self.en_count.store(0, Ordering::SeqCst);
|
|
}
|
|
|
|
fn en_nonce(&self) -> Nonce {
|
|
let count = self.en_count.fetch_add(1, Ordering::SeqCst);
|
|
tracing::trace!("encrypted count {}", count);
|
|
nonce_from_number(count)
|
|
}
|
|
|
|
fn de_nonce(&self) -> Nonce {
|
|
let count = self.de_count.fetch_add(1, Ordering::SeqCst);
|
|
tracing::trace!("decrypted count {}", count);
|
|
nonce_from_number(count)
|
|
}
|
|
}
|
|
|
|
/// Generates a nonce from a given number
|
|
/// The given number is repeated to fit the nonce bytes
|
|
fn nonce_from_number(number: u64) -> Nonce {
|
|
let number_bytes: [u8; 8] = number.to_be_bytes();
|
|
let num_vec = number_bytes.repeat(2);
|
|
let mut nonce_bytes = [0u8; 12];
|
|
nonce_bytes.copy_from_slice(&num_vec[..12]);
|
|
|
|
nonce_bytes.into()
|
|
}
|
|
|
|
impl<T: AsyncProtocolStream> EncryptedStream<T> {
|
|
/// Does a server-client key exchange.
|
|
/// 1. The server receives the public key of the client
|
|
/// 2. The server sends its own public key
|
|
/// 3. The server creates an intermediary encrypted connection
|
|
/// 4. The server generates a new secret
|
|
/// 5. The server sends the secret to the client
|
|
/// 6. The connection is upgraded with the new shared key
|
|
pub async fn from_server_key_exchange(mut inner: T, keys: &Keys) -> IPCResult<Self> {
|
|
let other_pub = receive_public_key(&mut inner).await?;
|
|
tracing::debug!("received peer public key {:?}", other_pub);
|
|
|
|
if !keys.allow_unknown && !keys.known_peers.contains(&other_pub) {
|
|
return Err(IPCError::UnknownPeer(other_pub));
|
|
}
|
|
send_public_key(&mut inner, &keys.secret).await?;
|
|
let shared_secret = keys.secret.diffie_hellman(&other_pub);
|
|
let mut stream = Self::new(inner, shared_secret);
|
|
let permanent_secret = generate_secret();
|
|
stream.write_all(&permanent_secret).await?;
|
|
stream.flush().await?;
|
|
stream.update_key(permanent_secret.into());
|
|
tracing::debug!("Connection established");
|
|
|
|
Ok(stream)
|
|
}
|
|
|
|
/// Does a client-server key exchange.
|
|
/// 1. The client sends its public key to the server
|
|
/// 2. The client receives the servers public key
|
|
/// 3. The client creates an intermediary encrypted connection
|
|
/// 4. The client receives the new key from the server
|
|
/// 5. The connection is upgraded with the new shared key
|
|
pub async fn from_client_key_exchange(mut inner: T, keys: &Keys) -> IPCResult<Self> {
|
|
send_public_key(&mut inner, &keys.secret).await?;
|
|
let other_pub = receive_public_key(&mut inner).await?;
|
|
tracing::debug!("received peer public key {:?}", other_pub);
|
|
|
|
if !keys.allow_unknown && !keys.known_peers.contains(&other_pub) {
|
|
return Err(IPCError::UnknownPeer(other_pub));
|
|
}
|
|
let shared_secret = keys.secret.diffie_hellman(&other_pub);
|
|
let mut stream = Self::new(inner, shared_secret);
|
|
let mut key_buf = vec![0u8; 32];
|
|
stream.read_exact(&mut key_buf).await?;
|
|
stream.update_key(key_buf.into());
|
|
tracing::debug!("Connection established");
|
|
|
|
Ok(stream)
|
|
}
|
|
}
|
|
|
|
#[tracing::instrument(level = "debug", skip_all)]
|
|
async fn receive_public_key<T: AsyncProtocolStream>(stream: &mut T) -> IPCResult<PublicKey> {
|
|
let mut pk_buf = [0u8; 32];
|
|
stream.read_exact(&mut pk_buf).await?;
|
|
|
|
Ok(PublicKey::from(pk_buf))
|
|
}
|
|
|
|
#[tracing::instrument(level = "debug", skip_all)]
|
|
async fn send_public_key<T: AsyncProtocolStream>(
|
|
stream: &mut T,
|
|
secret: &StaticSecret,
|
|
) -> IPCResult<()> {
|
|
let own_pk = PublicKey::from(secret);
|
|
stream.write_all(own_pk.as_bytes()).await?;
|
|
stream.flush().await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
|
fn generate_secret() -> Vec<u8> {
|
|
let mut rng = thread_rng();
|
|
let mut buf = vec![0u8; 32];
|
|
rng.fill_bytes(&mut buf);
|
|
|
|
Sha256::digest(&buf).to_vec()
|
|
}
|