send poly1305 hashes as well (message integrity)
This commit is contained in:
parent
4bef2ecc9c
commit
df5be01dfe
3 changed files with 68 additions and 28 deletions
|
|
@ -3,10 +3,10 @@ i mek protokol (pls don't guillotine me, is for fun)
|
||||||
|
|
||||||
## bad things (*that i should really fix*)
|
## bad things (*that i should really fix*)
|
||||||
- no verification of authenticity
|
- no verification of authenticity
|
||||||
- [ ] message/MAC ([poly1305](https://en.wikipedia.org/wiki/Poly1305)?)
|
- [x] message/MAC ([poly1305](https://en.wikipedia.org/wiki/Poly1305)?)
|
||||||
- [ ] server/client (static keys)
|
- [ ] server/client (static keys)
|
||||||
- protocol structure
|
- protocol structure
|
||||||
- [ ] how to prevent nonce reuse/how to get shared nonce? (current version uses one nonce for the entire connection :P)
|
- [x] how to prevent nonce reuse/how to get shared nonce? (current version uses one nonce for the entire connection :P)
|
||||||
- pseudorandom function? (but how would you seed it??? shared key?)
|
- pseudorandom function? (but how would you seed it??? shared key?)
|
||||||
- you would need to seed it with something else than *just* the shared key, or else the output would be completely predictable and an attacker could easily decrypt all your traffic if they manage to get the secret key (if they get the secret key, then your data is probably screwed anyways). however, if the nonce is ephemeral (not saved), and reasonably unpredictable for an attacker, then that would mean they would have to search up to $2^{96}$ blocks in order to get the data. its *nothing* compared to the secret key ($2^{256}$), and it is *not* where the security of the protocol or the chacha stream cipher should be derived.
|
- you would need to seed it with something else than *just* the shared key, or else the output would be completely predictable and an attacker could easily decrypt all your traffic if they manage to get the secret key (if they get the secret key, then your data is probably screwed anyways). however, if the nonce is ephemeral (not saved), and reasonably unpredictable for an attacker, then that would mean they would have to search up to $2^{96}$ blocks in order to get the data. its *nothing* compared to the secret key ($2^{256}$), and it is *not* where the security of the protocol or the chacha stream cipher should be derived.
|
||||||
- seeding it with the mac or using the hash of the previous message(s) would mean that if they were to replay or repeat the same message blocks, then an attacker would be able to see the same pattern of messages. it would also make it vulnerable to replay attacks, where an attacker could replay a message to the server, given that they somehow know the same nonce is reused.
|
- seeding it with the mac or using the hash of the previous message(s) would mean that if they were to replay or repeat the same message blocks, then an attacker would be able to see the same pattern of messages. it would also make it vulnerable to replay attacks, where an attacker could replay a message to the server, given that they somehow know the same nonce is reused.
|
||||||
|
|
@ -23,4 +23,5 @@ i mek protokol (pls don't guillotine me, is for fun)
|
||||||
|
|
||||||
## things that would be sick but also idk if i have the time or willpower before my attention gets taken by other shit i really want to do like implementing a post-quantum secure KEM (maybe KYBER..)
|
## things that would be sick but also idk if i have the time or willpower before my attention gets taken by other shit i really want to do like implementing a post-quantum secure KEM (maybe KYBER..)
|
||||||
- [ ] custom blake3 implementation (~ *IM NOT A BIG FAN OF THE GOVERNMENT* ~)
|
- [ ] custom blake3 implementation (~ *IM NOT A BIG FAN OF THE GOVERNMENT* ~)
|
||||||
- [ ] custom poly1305 implementation (it would be fine to use one from `crates.io`, but it would *be so awesome, it would be so cool* if i made it myself :D + fun)
|
- [x] custom poly1305 implementation (it would be fine to use one from `crates.io`, but it would *be so awesome, it would be so cool* if i made it myself :D + fun)
|
||||||
|
- that was actually surprisingly simple
|
||||||
75
src/lib.rs
75
src/lib.rs
|
|
@ -1,5 +1,7 @@
|
||||||
|
use crate::EncryptionError::IO;
|
||||||
use chacha::Block;
|
use chacha::Block;
|
||||||
use diffie_hellman::{Public, Secret};
|
use diffie_hellman::{Public, Secret};
|
||||||
|
use poly1305::oneoff_authenticate;
|
||||||
use rand::{RngCore, rng};
|
use rand::{RngCore, rng};
|
||||||
use std::io::{Error, Read, Write};
|
use std::io::{Error, Read, Write};
|
||||||
use std::net::{Shutdown, TcpStream};
|
use std::net::{Shutdown, TcpStream};
|
||||||
|
|
@ -20,6 +22,12 @@ pub struct EncryptedStream {
|
||||||
const KEY_DERIVATION_CONTEXT: &str =
|
const KEY_DERIVATION_CONTEXT: &str =
|
||||||
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EncryptionError {
|
||||||
|
InvalidMAC,
|
||||||
|
IO(Error),
|
||||||
|
}
|
||||||
|
|
||||||
impl EncryptedStream {
|
impl EncryptedStream {
|
||||||
/// connect and negotiate an encrypted stream as a client
|
/// connect and negotiate an encrypted stream as a client
|
||||||
pub fn connect(address: &str) -> Result<EncryptedStream, Error> {
|
pub fn connect(address: &str) -> Result<EncryptedStream, Error> {
|
||||||
|
|
@ -37,25 +45,19 @@ impl EncryptedStream {
|
||||||
|
|
||||||
let shared_secret = secret.diffie_hellman(&their_public);
|
let shared_secret = secret.diffie_hellman(&their_public);
|
||||||
|
|
||||||
let mut nonce_bytes = [0u8; 12];
|
|
||||||
socket.read_exact(&mut nonce_bytes)?;
|
|
||||||
|
|
||||||
let key =
|
let key =
|
||||||
shared_secret.derive_key(|bytes| blake3::derive_key(KEY_DERIVATION_CONTEXT, &bytes));
|
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 {
|
pub fn wrap(socket: TcpStream, key: [u8; 32]) -> EncryptedStream {
|
||||||
let mut nonce_u32s = [0u32; 3];
|
|
||||||
u8_to_u32(&mut nonce_u32s, &nonce);
|
|
||||||
|
|
||||||
let mut key_u32s = [0u32; 8];
|
let mut key_u32s = [0u32; 8];
|
||||||
u8_to_u32(&mut key_u32s, &key);
|
u8_to_u32(&mut key_u32s, &key);
|
||||||
|
|
||||||
EncryptedStream {
|
EncryptedStream {
|
||||||
socket,
|
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)
|
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
|
// the first bytes should be the nonce
|
||||||
let mut nonce_bytes = [0u8; 12];
|
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];
|
let mut nonce = [0u32; 3];
|
||||||
u8_to_u32(&mut nonce, &nonce_bytes);
|
u8_to_u32(&mut nonce, &nonce_bytes);
|
||||||
|
|
@ -80,13 +84,53 @@ impl EncryptedStream {
|
||||||
let poly_key = self.block.get_bytes();
|
let poly_key = self.block.get_bytes();
|
||||||
self.block.advance();
|
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];
|
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 len = u32::from_le_bytes(len_bytes);
|
||||||
|
|
||||||
let mut buf = vec![0u8; len as usize];
|
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 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 =
|
let key =
|
||||||
shared_secret.derive_key(|bytes| blake3::derive_key(KEY_DERIVATION_CONTEXT, &bytes));
|
shared_secret.derive_key(|bytes| blake3::derive_key(KEY_DERIVATION_CONTEXT, &bytes));
|
||||||
|
|
||||||
Ok(EncryptedStream::wrap(socket, key, nonce_bytes))
|
Ok(EncryptedStream::wrap(socket, key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -1,6 +1,6 @@
|
||||||
use hermes::EncryptedStream;
|
use hermes::EncryptedStream;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::io::{Read, Write};
|
use std::io::Write;
|
||||||
use std::net::{Shutdown, TcpListener};
|
use std::net::{Shutdown, TcpListener};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
|
@ -22,7 +22,9 @@ fn main() {
|
||||||
println!("successfully encrypted stream");
|
println!("successfully encrypted stream");
|
||||||
|
|
||||||
encrypted
|
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();
|
.unwrap();
|
||||||
println!("successfully wrote data");
|
println!("successfully wrote data");
|
||||||
|
|
||||||
|
|
@ -32,13 +34,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"c" => {
|
"c" => {
|
||||||
let mut client = EncryptedStream::connect("127.0.0.1:2007").unwrap();
|
let mut client = EncryptedStream::connect("127.0.0.1:2007").expect("Failed to connect");
|
||||||
println!("successfully connected client");
|
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let buf = client.read_packet().unwrap();
|
||||||
client.read_to_end(&mut buf).unwrap();
|
|
||||||
println!("successfully read data");
|
|
||||||
|
|
||||||
|
println!("success!");
|
||||||
println!("data: {}", String::from_utf8_lossy(&buf));
|
println!("data: {}", String::from_utf8_lossy(&buf));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue