#![allow(missing_docs)]
use aes_gcm::{
aead::{Aead, Payload},
Aes256Gcm, KeyInit,
};
use sha2::{Digest, Sha256};
use std::net::SocketAddr;
use crate::error::ProtocolError;
pub const COOKIE_SECRET: &[u8; 32] = b"arc_protocol_stateless_cookie\x00\x00\x00";
pub fn compute_cookie(addr: &SocketAddr) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(COOKIE_SECRET);
hasher.update(addr.ip().to_string().as_bytes());
hasher.update(addr.port().to_le_bytes());
let mut cookie = [0u8; 32];
cookie.copy_from_slice(&hasher.finalize());
cookie
}
pub fn verify_cookie(addr: &SocketAddr, cookie: &[u8; 32]) -> bool {
&compute_cookie(addr) == cookie
}
pub struct CryptoContext {
tx: Aes256Gcm,
rx: Aes256Gcm,
}
impl CryptoContext {
pub fn new(tx_key: &[u8; 32], rx_key: &[u8; 32]) -> Self {
Self {
tx: Aes256Gcm::new_from_slice(tx_key).unwrap(),
rx: Aes256Gcm::new_from_slice(rx_key).unwrap(),
}
}
fn make_nonce(&self, conn_id: u32, seq: u32) -> [u8; 12] {
let mut nonce = [0u8; 12];
nonce[0..4].copy_from_slice(&conn_id.to_be_bytes());
nonce[4..8].copy_from_slice(&seq.to_be_bytes());
nonce[8..12].copy_from_slice(&[0u8; 4]);
nonce
}
pub fn encrypt(
&self,
conn_id: u32,
seq: u32,
header: &[u8],
payload: &[u8],
) -> Result<Vec<u8>, ProtocolError> {
let nonce = self.make_nonce(conn_id, seq);
let ciphertext = self
.tx
.encrypt(
&nonce.into(),
Payload {
msg: payload,
aad: header,
},
)
.map_err(|_| ProtocolError::CryptoFailure)?;
Ok([header, &ciphertext].concat())
}
pub fn decrypt(
&self,
conn_id: u32,
seq: u32,
header: &[u8],
encrypted: &[u8],
) -> Result<Vec<u8>, ProtocolError> {
let nonce = self.make_nonce(conn_id, seq);
self.rx
.decrypt(
&nonce.into(),
Payload {
msg: encrypted,
aad: header,
},
)
.map_err(|_| ProtocolError::CryptoFailure)
}
}