send poly1305 hashes as well (message integrity)

This commit is contained in:
Neemek 2025-11-08 18:35:19 +01:00
parent 4bef2ecc9c
commit df5be01dfe
3 changed files with 68 additions and 28 deletions

View file

@ -1,5 +1,7 @@
use crate::EncryptionError::IO;
use chacha::Block;
use diffie_hellman::{Public, Secret};
use poly1305::oneoff_authenticate;
use rand::{RngCore, rng};
use std::io::{Error, Read, Write};
use std::net::{Shutdown, TcpStream};
@ -20,6 +22,12 @@ pub struct EncryptedStream {
const KEY_DERIVATION_CONTEXT: &str =
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
#[derive(Debug)]
pub enum EncryptionError {
InvalidMAC,
IO(Error),
}
impl EncryptedStream {
/// connect and negotiate an encrypted stream as a client
pub fn connect(address: &str) -> Result<EncryptedStream, Error> {
@ -37,25 +45,19 @@ impl EncryptedStream {
let shared_secret = secret.diffie_hellman(&their_public);
let mut nonce_bytes = [0u8; 12];
socket.read_exact(&mut nonce_bytes)?;
let key =
shared_secret.derive_key(|bytes| blake3::derive_key(KEY_DERIVATION_CONTEXT, &bytes));
Ok(EncryptedStream::wrap(socket, key, nonce_bytes))
Ok(EncryptedStream::wrap(socket, key))
}
pub fn wrap(socket: TcpStream, key: [u8; 32], nonce: [u8; 12]) -> EncryptedStream {
let mut nonce_u32s = [0u32; 3];
u8_to_u32(&mut nonce_u32s, &nonce);
pub fn wrap(socket: TcpStream, key: [u8; 32]) -> EncryptedStream {
let mut key_u32s = [0u32; 8];
u8_to_u32(&mut key_u32s, &key);
EncryptedStream {
socket,
block: Block::new(key_u32s, 0, nonce_u32s, 20),
block: Block::new(key_u32s, 0, [0u32; 3], 20),
}
}
@ -67,10 +69,12 @@ impl EncryptedStream {
self.block.reset(next_nonce)
}
pub fn read_packet(&mut self) -> Result<Vec<u8>, Error> {
pub fn read_packet(&mut self) -> Result<Vec<u8>, EncryptionError> {
// the first bytes should be the nonce
let mut nonce_bytes = [0u8; 12];
self.socket.read(&mut nonce_bytes)?;
if let Err(err) = self.socket.read(&mut nonce_bytes) {
return Err(IO(err));
}
let mut nonce = [0u32; 3];
u8_to_u32(&mut nonce, &nonce_bytes);
@ -80,13 +84,53 @@ impl EncryptedStream {
let poly_key = self.block.get_bytes();
self.block.advance();
let mut mac = [0u8; 16];
if let Err(err) = self.socket.read(&mut mac) {
return Err(IO(err));
}
let mut len_bytes = [0u8; 4];
self.read(&mut len_bytes)?;
if let Err(err) = self.socket.read(&mut len_bytes) {
return Err(IO(err));
}
let len = u32::from_le_bytes(len_bytes);
let mut buf = vec![0u8; len as usize];
if let Err(err) = self.read(&mut buf) {
return Err(IO(err));
}
let my_mac = oneoff_authenticate(&buf, &poly_key[0..32].try_into().unwrap());
if mac != my_mac {
return Err(EncryptionError::InvalidMAC);
}
Ok(buf)
}
pub fn write_packet(&mut self, buf: &[u8]) -> Result<(), Error> {
let mut nonce_bytes = [0u8; 12];
rng().fill_bytes(&mut nonce_bytes);
let mut nonce = [0u32; 3];
u8_to_u32(&mut nonce, &nonce_bytes);
self.block.reset(nonce);
let poly_key: [u8; 32] = self.block.get_bytes()[0..32].try_into().unwrap();
self.block.advance();
let mac = oneoff_authenticate(&buf, &poly_key);
self.socket.write(&nonce_bytes)?;
self.socket.write(&mac)?;
self.socket.write(&(buf.len() as u32).to_le_bytes())?;
self.write(&buf)?;
Ok(())
}
}
@ -107,15 +151,10 @@ impl TryFrom<TcpStream> for EncryptedStream {
let shared_secret = secret.diffie_hellman(&their_public);
let mut nonce_bytes = [0u8; 12];
rng().fill_bytes(&mut nonce_bytes);
socket.write(&nonce_bytes)?;
let key =
shared_secret.derive_key(|bytes| blake3::derive_key(KEY_DERIVATION_CONTEXT, &bytes));
Ok(EncryptedStream::wrap(socket, key, nonce_bytes))
Ok(EncryptedStream::wrap(socket, key))
}
}

View file

@ -1,6 +1,6 @@
use hermes::EncryptedStream;
use std::env::args;
use std::io::{Read, Write};
use std::io::Write;
use std::net::{Shutdown, TcpListener};
use std::process::exit;
@ -22,7 +22,9 @@ fn main() {
println!("successfully encrypted stream");
encrypted
.write(b"the fitness gram pacer test is a multi-stage aerobic fitness test...")
.write_packet(
b"the fitness gram pacer test is a multi-stage aerobic fitness test...",
)
.unwrap();
println!("successfully wrote data");
@ -32,13 +34,11 @@ fn main() {
}
}
"c" => {
let mut client = EncryptedStream::connect("127.0.0.1:2007").unwrap();
println!("successfully connected client");
let mut client = EncryptedStream::connect("127.0.0.1:2007").expect("Failed to connect");
let mut buf = Vec::new();
client.read_to_end(&mut buf).unwrap();
println!("successfully read data");
let buf = client.read_packet().unwrap();
println!("success!");
println!("data: {}", String::from_utf8_lossy(&buf));
}
_ => {