use crate::crypto::{
CONSTRUCTION, IDENTIFIER, PrivateKey, PublicKey25519, aead_decrypt, aead_encrypt, dh,
dh_generate, hash, kdf1, kdf2, timestamp,
};
use crate::messages::{
HandshakeInitiation, HandshakeResponse, MESSAGE_HANDSHAKE_INITIATION,
MESSAGE_HANDSHAKE_RESPONSE,
};
use crate::{Result, WireGuardError};
#[derive(Debug, Clone)]
pub struct SessionKeys {
pub send_key: [u8; 32],
pub recv_key: [u8; 32],
}
#[derive(Debug)]
pub struct InitiatorState {
chaining_key: [u8; 32],
hash: [u8; 32],
local_static_private: PrivateKey,
local_ephemeral_private: Option<PrivateKey>,
remote_static_public: PublicKey25519,
preshared_key: Option<[u8; 32]>,
initiation_sent: bool,
}
impl InitiatorState {
pub fn new(
local_static_private: PrivateKey,
remote_static_public: PublicKey25519,
preshared_key: Option<[u8; 32]>,
) -> Self {
let chaining_key = hash(CONSTRUCTION);
let mut h = hash(&[chaining_key.as_slice(), IDENTIFIER].concat());
h = hash(&[h.as_slice(), &remote_static_public].concat());
Self {
chaining_key,
hash: h,
local_static_private,
local_ephemeral_private: None,
remote_static_public,
preshared_key,
initiation_sent: false,
}
}
pub fn create_initiation(&mut self) -> Result<HandshakeInitiation> {
if self.initiation_sent {
return Err(WireGuardError::ProtocolError(
"Initiation already sent".to_string(),
));
}
let (ephemeral_private, ephemeral_public) = dh_generate();
self.local_ephemeral_private = Some(ephemeral_private);
self.chaining_key = kdf1(&self.chaining_key, &ephemeral_public);
self.hash = hash(&[self.hash.as_slice(), &ephemeral_public].concat());
let dh_es = dh(&ephemeral_private, &self.remote_static_public);
let (chaining_key, temp_key1) = kdf2(&self.chaining_key, &dh_es);
self.chaining_key = chaining_key;
let local_static_public = self.derive_public_key()?;
let static_encrypted = aead_encrypt(&temp_key1, 0, &local_static_public, &self.hash)?;
self.hash = hash(&[self.hash.as_slice(), &static_encrypted].concat());
let dh_ss = dh(&self.local_static_private, &self.remote_static_public);
let (chaining_key, temp_key2) = kdf2(&self.chaining_key, &dh_ss);
self.chaining_key = chaining_key;
let ts = timestamp();
let timestamp_encrypted = aead_encrypt(&temp_key2, 0, &ts, &self.hash)?;
self.hash = hash(&[self.hash.as_slice(), ×tamp_encrypted].concat());
self.initiation_sent = true;
Ok(HandshakeInitiation {
message_type: MESSAGE_HANDSHAKE_INITIATION,
reserved: [0; 3],
sender: rand::random::<u32>(), ephemeral: ephemeral_public,
static_encrypted,
timestamp_encrypted,
mac1: [0; 16], mac2: [0; 16], })
}
pub fn process_response(&mut self, response: &HandshakeResponse) -> Result<SessionKeys> {
if !self.initiation_sent {
return Err(WireGuardError::ProtocolError(
"No initiation sent yet".to_string(),
));
}
if response.message_type != MESSAGE_HANDSHAKE_RESPONSE {
return Err(WireGuardError::ProtocolError(
"Invalid message type".to_string(),
));
}
let local_ephemeral_private = self.local_ephemeral_private.ok_or_else(|| {
WireGuardError::ProtocolError("Missing ephemeral private key".to_string())
})?;
self.chaining_key = kdf1(&self.chaining_key, &response.ephemeral);
self.hash = hash(&[self.hash.as_slice(), &response.ephemeral].concat());
let dh_ee = dh(&local_ephemeral_private, &response.ephemeral);
self.chaining_key = kdf1(&self.chaining_key, &dh_ee);
let dh_se = dh(&self.local_static_private, &response.ephemeral);
self.chaining_key = kdf1(&self.chaining_key, &dh_se);
if let Some(psk) = self.preshared_key {
let temp_key = kdf1(&self.chaining_key, &psk);
let (chaining_key, temp_key2) = kdf2(&temp_key, &[]);
self.chaining_key = chaining_key;
self.hash = hash(&[self.hash.as_slice(), &temp_key2].concat());
}
let (chaining_key, temp_key) = kdf2(&self.chaining_key, &[]);
self.chaining_key = chaining_key;
let empty_decrypted = aead_decrypt(&temp_key, 0, &response.empty_encrypted, &self.hash)?;
if !empty_decrypted.is_empty() {
return Err(WireGuardError::ProtocolError(
"Expected empty payload".to_string(),
));
}
self.hash = hash(&[self.hash.as_slice(), &response.empty_encrypted].concat());
let (send_key, recv_key) = kdf2(&self.chaining_key, &[]);
Ok(SessionKeys { send_key, recv_key })
}
pub fn remote_static_public(&self) -> PublicKey25519 {
self.remote_static_public
}
fn derive_public_key(&self) -> Result<PublicKey25519> {
use x25519_dalek::{PublicKey, StaticSecret};
let secret = StaticSecret::from(self.local_static_private);
let public = PublicKey::from(&secret);
Ok(public.to_bytes())
}
}