From 67a935f291870a51bd591979c193e06fce2fa843 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 18 Sep 2019 11:53:13 +0200 Subject: [PATCH] Padding --- Cargo.toml | 1 + src/crypto.rs | 37 ++++++++++++++++++++++++++++++++----- src/dnscrypt.rs | 16 ++++++++++++++-- src/main.rs | 22 +++++++++++++--------- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7a41a08..5ab5fb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ log = "0.4.8" net2 = "0.2.33" parking_lot = "0.9.0" rand = "0.7.2" +siphasher = "0.3.0" tokio = "=0.2.0-alpha.4" tokio-net = "=0.2.0-alpha.4" diff --git a/src/crypto.rs b/src/crypto.rs index ba1bb08..ceba407 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,7 +1,9 @@ use crate::errors::*; use libsodium_sys::*; +use siphasher::sip::SipHasher13; use std::ffi::CStr; +use std::hash::Hasher; use std::ptr; #[allow(non_upper_case_globals)] @@ -187,14 +189,39 @@ impl SharedKey { &self, target: &mut Vec, nonce: &[u8], - mut plaintext: Vec, + client_nonce: &[u8], + plaintext: Vec, + max_target_size: usize, ) -> Result<(), Error> { - plaintext.push(0x80); + ensure!( + max_target_size >= crypto_box_curve25519xchacha20poly1305_MACBYTES as usize, + "Max target size too small" + ); let plaintext_len = plaintext.len(); + let max_padded_plaintext_len = + max_target_size - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize; + let mut hasher = SipHasher13::new(); + hasher.write(&self.0); + hasher.write(&client_nonce); + let pad_size: usize = 1 + (hasher.finish() as usize & 0xff); + let mut padded_plaintext_len = (plaintext_len + pad_size) & !63; + if padded_plaintext_len < plaintext_len { + padded_plaintext_len += 64; + } + if padded_plaintext_len > max_padded_plaintext_len { + padded_plaintext_len = max_padded_plaintext_len; + } + ensure!(padded_plaintext_len > plaintext_len, "No room for padding"); + let mut padded_plaintext = plaintext; + padded_plaintext.push(0x80); + while padded_plaintext.len() != padded_plaintext_len { + padded_plaintext.push(0x00); + } + let padded_plaintext_len = padded_plaintext.len(); let target_header_len = target.len(); target.resize( target_header_len - + plaintext_len + + padded_plaintext_len + crypto_box_curve25519xchacha20poly1305_MACBYTES as usize, 0, ); @@ -202,8 +229,8 @@ impl SharedKey { let res = unsafe { libsodium_sys::crypto_box_curve25519xchacha20poly1305_easy_afternm( encrypted.as_mut_ptr(), - plaintext.as_ptr(), - plaintext_len as _, + padded_plaintext.as_ptr(), + padded_plaintext_len as _, nonce.as_ptr(), self.0.as_ptr(), ) diff --git a/src/dnscrypt.rs b/src/dnscrypt.rs index ae51c0d..a4e3e9d 100644 --- a/src/dnscrypt.rs +++ b/src/dnscrypt.rs @@ -66,7 +66,7 @@ pub fn decrypt( .ok_or_else(|| format_err!("Client magic not found"))?; let mut nonce = [0u8; DNSCRYPT_FULL_NONCE_SIZE as usize]; - &mut nonce[..DNSCRYPT_QUERY_NONCE_SIZE].copy_from_slice(client_nonce); + nonce[..DNSCRYPT_QUERY_NONCE_SIZE].copy_from_slice(client_nonce); let resolver_kp = dnscrypt_encryption_params.resolver_kp(); let shared_key = resolver_kp.compute_shared_key(client_pk)?; let packet = shared_key.decrypt(&nonce, encrypted_packet)?; @@ -79,10 +79,22 @@ pub fn encrypt( packet: Vec, shared_key: &SharedKey, nonce: &[u8; DNSCRYPT_FULL_NONCE_SIZE as usize], + max_packet_size: usize, ) -> Result, Error> { let mut wrapped_packet = Vec::with_capacity(DNS_MAX_PACKET_SIZE); wrapped_packet.extend_from_slice(&[0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38]); wrapped_packet.extend_from_slice(nonce); - shared_key.encrypt_into(&mut wrapped_packet, nonce, packet)?; + ensure!( + max_packet_size >= wrapped_packet.len(), + "Max packet size too short" + ); + let max_encrypted_size = max_packet_size - wrapped_packet.len(); + shared_key.encrypt_into( + &mut wrapped_packet, + nonce, + &nonce[..DNSCRYPT_QUERY_NONCE_SIZE], + packet, + max_encrypted_size, + )?; Ok(wrapped_packet) } diff --git a/src/main.rs b/src/main.rs index 7547cf5..0504bb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,12 +73,11 @@ fn maybe_truncate_response( client_ctx: &ClientCtx, packet: Vec, response: Vec, - original_packet_len: usize, + original_packet_size: usize, ) -> Result, Error> { if let ClientCtx::Udp(_) = client_ctx { - let encrypted_response_min_len = - response.len() + DNSCRYPT_RESPONSE_HEADER_SIZE + DNSCRYPT_RESPONSE_MIN_PADDING_SIZE; - if encrypted_response_min_len > original_packet_len + let encrypted_response_min_len = response.len() + DNSCRYPT_RESPONSE_MIN_OVERHEAD; + if encrypted_response_min_len > original_packet_size || encrypted_response_min_len > DNSCRYPT_UDP_RESPONSE_MAX_SIZE { return Ok(dns::serve_truncated(packet)?); @@ -91,17 +90,22 @@ async fn respond_to_query( client_ctx: ClientCtx, packet: Vec, response: Vec, - original_packet_len: usize, + original_packet_size: usize, shared_key: Option, nonce: Option<[u8; DNSCRYPT_FULL_NONCE_SIZE]>, ) -> Result<(), Error> { ensure!(dns::is_response(&response), "Packet is not a response"); + let max_response_size = match client_ctx { + ClientCtx::Udp(_) => original_packet_size, + ClientCtx::Tcp(_) => DNSCRYPT_TCP_RESPONSE_MAX_SIZE, + }; let response = match &shared_key { None => response, Some(shared_key) => dnscrypt::encrypt( - maybe_truncate_response(&client_ctx, packet, response, original_packet_len)?, + maybe_truncate_response(&client_ctx, packet, response, original_packet_size)?, shared_key, nonce.as_ref().unwrap(), + max_response_size, )?, }; match client_ctx { @@ -131,7 +135,7 @@ async fn handle_client_query( client_ctx: ClientCtx, encrypted_packet: Vec, ) -> Result<(), Error> { - let original_packet_len = encrypted_packet.len(); + let original_packet_size = encrypted_packet.len(); let (shared_key, nonce, mut packet) = match dnscrypt::decrypt(&encrypted_packet, &globals.dnscrypt_encryption_params_set) { Ok(x) => x, @@ -146,7 +150,7 @@ async fn handle_client_query( client_ctx, packet, synth_packet, - original_packet_len, + original_packet_size, None, None, ) @@ -217,7 +221,7 @@ async fn handle_client_query( client_ctx, packet, response, - original_packet_len, + original_packet_size, Some(shared_key), Some(nonce), )