2
0
mirror of https://github.com/jedisct1/encrypted-dns-server synced 2024-11-12 13:10:44 +00:00

Start decrypting DNSCrypt queries

This commit is contained in:
Frank Denis 2019-09-17 22:33:15 +02:00
parent 58c5452084
commit 5f0bb6daf5
6 changed files with 229 additions and 30 deletions

View File

@ -4,6 +4,10 @@ use libsodium_sys::*;
use std::ffi::CStr;
use std::ptr;
#[allow(non_upper_case_globals)]
pub const crypto_box_curve25519xchacha20poly1305_HALFNONCEBYTES: usize =
crypto_box_curve25519xchacha20poly1305_NONCEBYTES as usize / 2;
#[derive(Derivative)]
#[derivative(Default)]
pub struct Signature(
@ -86,6 +90,84 @@ impl SignKeyPair {
}
}
#[derive(Debug, Default)]
pub struct CryptSK([u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize]);
impl CryptSK {
pub fn as_bytes(
&self,
) -> &[u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize] {
&self.0
}
pub fn from_bytes(
bytes: [u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize],
) -> Self {
CryptSK(bytes)
}
}
#[derive(Debug, Default)]
pub struct CryptPK([u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize]);
impl CryptPK {
pub fn as_bytes(
&self,
) -> &[u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize] {
&self.0
}
pub fn from_bytes(
bytes: [u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize],
) -> Self {
CryptPK(bytes)
}
}
#[derive(Debug, Default)]
pub struct CryptKeyPair {
pub sk: CryptSK,
pub pk: CryptPK,
}
impl CryptKeyPair {
pub fn new() -> Self {
let mut kp = CryptKeyPair::default();
unsafe {
crypto_box_curve25519xchacha20poly1305_keypair(
kp.pk.0.as_mut_ptr(),
kp.sk.0.as_mut_ptr(),
)
};
kp
}
pub fn decrypt(
&self,
client_pk: &[u8],
nonce: &[u8],
encrypted: &[u8],
) -> Result<Vec<u8>, Error> {
let encrypted_len = encrypted.len();
let mut decrypted =
vec![0u8; encrypted_len - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize];
let res = unsafe {
libsodium_sys::crypto_box_curve25519xchacha20poly1305_open_easy(
decrypted.as_mut_ptr(),
encrypted.as_ptr(),
encrypted_len as _,
nonce.as_ptr(),
client_pk.as_ptr(),
self.sk.as_bytes().as_ptr(),
)
};
match res {
0 => Ok(decrypted),
_ => bail!("Unable to decrypt"),
}
}
}
pub fn bin2hex(bin: &[u8]) -> String {
let bin_len = bin.len();
let hex_len = bin_len * 2 + 1;

View File

@ -3,7 +3,7 @@ use crate::errors::*;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
pub const DNS_MAX_HOSTNAME_LEN: usize = 256;
pub const DNS_MAX_HOSTNAME_SIZE: usize = 256;
pub const DNS_HEADER_SIZE: usize = 12;
pub const DNS_OFFSET_FLAGS: usize = 2;
pub const DNS_MAX_PACKET_SIZE: usize = 8192;
@ -96,7 +96,7 @@ pub fn qname(packet: &[u8]) -> Result<Vec<u8>, Error> {
ensure!(qdcount(packet) == 1, "Unexpected query count");
let packet_len = packet.len();
let mut offset = DNS_HEADER_SIZE;
let mut qname = Vec::with_capacity(DNS_MAX_HOSTNAME_LEN);
let mut qname = Vec::with_capacity(DNS_MAX_HOSTNAME_SIZE);
let mut indirections = 0;
loop {
ensure!(offset < packet_len, "Short packet");
@ -127,7 +127,7 @@ pub fn qname(packet: &[u8]) -> Result<Vec<u8>, Error> {
qname.push(b'.')
}
ensure!(
qname.len() < DNS_MAX_HOSTNAME_LEN - label_len,
qname.len() < DNS_MAX_HOSTNAME_SIZE - label_len,
"Name too long"
);
qname.extend_from_slice(&packet[offset..offset + label_len]);
@ -157,7 +157,7 @@ fn skip_name(packet: &[u8], offset: usize) -> Result<usize, Error> {
"Malformed packet with an out-of-bounds name"
);
qname_len += label_len + 1;
ensure!(qname_len <= DNS_MAX_HOSTNAME_LEN, "Name too long");
ensure!(qname_len <= DNS_MAX_HOSTNAME_SIZE, "Name too long");
offset += label_len + 1;
if label_len == 0 {
break;
@ -293,7 +293,7 @@ pub fn set_edns_max_payload_size(packet: &mut Vec<u8>, max_payload_size: u16) ->
pub fn serve_certificates<'t>(
client_packet: &[u8],
expected_qname: &str,
dnscrypt_certs: impl IntoIterator<Item = &'t DNSCryptCert>,
dnscrypt_encryption_params_set: impl IntoIterator<Item = &'t DNSCryptEncryptionParams>,
) -> Result<Option<Vec<u8>>, Error> {
let offset = skip_name(client_packet, DNS_HEADER_SIZE)?;
ensure!(client_packet.len() - offset >= 4, "Short packet");
@ -309,9 +309,8 @@ pub fn serve_certificates<'t>(
}
let mut packet = (&client_packet[..offset + 4]).to_vec();
authoritative_response(&mut packet);
for dnscrypt_cert in dnscrypt_certs {
let cert_bin = dnscrypt_cert.as_bytes();
for dnscrypt_encryption_params in dnscrypt_encryption_params_set {
let cert_bin = dnscrypt_encryption_params.dnscrypt_cert().as_bytes();
ensure!(cert_bin.len() <= 0xff, "Certificate too long");
ancount_inc(&mut packet)?;
packet.write_u16::<BigEndian>(0xc000 + DNS_HEADER_SIZE as u16)?;

71
src/dnscrypt.rs Normal file
View File

@ -0,0 +1,71 @@
use crate::crypto::*;
use crate::dns::*;
use crate::dnscrypt_certs::*;
use crate::errors::*;
use libsodium_sys::*;
use std::ffi::CStr;
use std::ptr;
pub const DNSCRYPT_CLIENT_MAGIC_SIZE: usize = 8;
pub const DNSCRYPT_CLIENT_PK_SIZE: usize = 32;
pub const DNSCRYPT_CLIENT_NONCE_SIZE: usize = 12;
pub struct DNSCryptQuery<'t> {
wrapped_packet: &'t [u8],
}
impl<'t> DNSCryptQuery<'t> {
pub fn new(
wrapped_packet: &'t [u8],
dnscrypt_encryption_params_set: &[DNSCryptEncryptionParams],
) -> Result<Self, Error> {
ensure!(
wrapped_packet.len()
>= DNSCRYPT_CLIENT_MAGIC_SIZE
+ DNSCRYPT_CLIENT_PK_SIZE
+ DNSCRYPT_CLIENT_NONCE_SIZE
+ DNS_HEADER_SIZE,
"Short packet"
);
let dnscrypt_query = DNSCryptQuery { wrapped_packet };
let client_magic = dnscrypt_query.client_magic();
let dnscrypt_encryption_params = dnscrypt_encryption_params_set
.iter()
.find(|p| p.client_magic() == client_magic)
.ok_or_else(|| format_err!("Client magic not found"))?;
let encrypted_packet = dnscrypt_query.encrypted_packet();
let encrypted_packet_len = encrypted_packet.len();
let mut nonce = vec![0u8; crypto_box_curve25519xchacha20poly1305_NONCEBYTES as usize];
&mut nonce[..crypto_box_curve25519xchacha20poly1305_HALFNONCEBYTES]
.copy_from_slice(dnscrypt_query.client_nonce());
let resolver_kp = dnscrypt_encryption_params.resolver_kp();
resolver_kp.decrypt(dnscrypt_query.client_pk(), &nonce, encrypted_packet)?;
dbg!("ok");
Ok(dnscrypt_query)
}
pub fn client_magic(&self) -> &[u8] {
&self.wrapped_packet[..DNSCRYPT_CLIENT_MAGIC_SIZE]
}
pub fn client_pk(&self) -> &[u8] {
&self.wrapped_packet
[DNSCRYPT_CLIENT_MAGIC_SIZE..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE]
}
pub fn client_nonce(&self) -> &[u8] {
&self.wrapped_packet[DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE
..DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE]
}
pub fn encrypted_packet(&self) -> &[u8] {
&self.wrapped_packet
[DNSCRYPT_CLIENT_MAGIC_SIZE + DNSCRYPT_CLIENT_PK_SIZE + DNSCRYPT_CLIENT_NONCE_SIZE..]
}
pub fn into_packet(self) -> Vec<u8> {
self.encrypted_packet().to_vec()
}
}

View File

@ -41,7 +41,7 @@ pub struct DNSCryptCert {
}
impl DNSCryptCert {
pub fn new(resolver_kp: &SignKeyPair) -> Self {
pub fn new(provider_kp: &SignKeyPair, resolver_kp: &CryptKeyPair) -> Self {
let ts_start = now();
let ts_end = ts_start + 86400;
@ -63,7 +63,7 @@ impl DNSCryptCert {
BigEndian::write_u16(&mut dnscrypt_cert.minor_version, 0);
dnscrypt_cert.signature.copy_from_slice(
resolver_kp
provider_kp
.sk
.sign(dnscrypt_cert_inner.as_bytes())
.as_bytes(),
@ -74,4 +74,37 @@ impl DNSCryptCert {
pub fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of_val(self)) }
}
pub fn client_magic(&self) -> &[u8] {
&self.inner.client_magic
}
}
#[derive(Debug)]
pub struct DNSCryptEncryptionParams {
dnscrypt_cert: DNSCryptCert,
resolver_kp: CryptKeyPair,
}
impl DNSCryptEncryptionParams {
pub fn new(provider_kp: &SignKeyPair) -> Self {
let resolver_kp = CryptKeyPair::new();
let dnscrypt_cert = DNSCryptCert::new(&provider_kp, &resolver_kp);
DNSCryptEncryptionParams {
dnscrypt_cert,
resolver_kp,
}
}
pub fn client_magic(&self) -> &[u8] {
self.dnscrypt_cert.client_magic()
}
pub fn dnscrypt_cert(&self) -> &DNSCryptCert {
&self.dnscrypt_cert
}
pub fn resolver_kp(&self) -> &CryptKeyPair {
&self.resolver_kp
}
}

View File

@ -13,8 +13,7 @@ use tokio::sync::oneshot;
#[derive(Debug)]
pub struct Globals {
pub runtime: Arc<Runtime>,
pub resolver_kp: SignKeyPair,
pub dnscrypt_certs: Vec<DNSCryptCert>,
pub dnscrypt_encryption_params_set: Vec<DNSCryptEncryptionParams>,
pub provider_name: String,
pub listen_addr: SocketAddr,
pub external_addr: SocketAddr,

View File

@ -18,12 +18,14 @@ extern crate log;
mod config;
mod crypto;
mod dns;
mod dnscrypt;
mod dnscrypt_certs;
mod errors;
mod globals;
use crypto::*;
use dns::*;
use dnscrypt::*;
use dnscrypt_certs::*;
use errors::*;
use globals::*;
@ -94,19 +96,30 @@ async fn respond_to_query(client_ctx: ClientCtx, packet: Vec<u8>) -> Result<(),
async fn handle_client_query(
globals: Arc<Globals>,
client_ctx: ClientCtx,
mut packet: Vec<u8>,
encrypted_packet: Vec<u8>,
) -> Result<(), Error> {
ensure!(packet.len() >= DNSCRYPT_QUERY_MIN_SIZE, "Short packet");
ensure!(dns::qdcount(&packet) == 1, "No question");
ensure!(
!dns::is_response(&packet),
"Question expected, but got a response instead"
);
if let Some(synth_packet) =
serve_certificates(&packet, &globals.provider_name, &globals.dnscrypt_certs)?
{
return respond_to_query(client_ctx, synth_packet).await;
}
let packet = DNSCryptQuery::new(&encrypted_packet, &globals.dnscrypt_encryption_params_set);
let mut packet = match packet {
Err(_) => {
let packet = encrypted_packet;
ensure!(packet.len() >= DNS_HEADER_SIZE, "Short packet");
ensure!(dns::qdcount(&packet) == 1, "No question");
ensure!(
!dns::is_response(&packet),
"Question expected, but got a response instead"
);
if let Some(synth_packet) = serve_certificates(
&packet,
&globals.provider_name,
&globals.dnscrypt_encryption_params_set,
)? {
return respond_to_query(client_ctx, synth_packet).await;
}
bail!("Unencrypted query");
}
Ok(packet) => packet.into_packet(),
};
let original_tid = dns::tid(&packet);
let tid = random();
dns::set_tid(&mut packet, tid);
@ -317,15 +330,15 @@ fn main() -> Result<(), Error> {
let udp_timeout = Duration::from_secs(10);
let tcp_timeout = Duration::from_secs(10);
let resolver_kp = SignKeyPair::new();
let provider_kp = SignKeyPair::new();
info!("Server address: {}", listen_addr);
info!("Provider public key: {}", resolver_kp.pk.as_string());
info!("Provider public key: {}", provider_kp.pk.as_string());
info!("Provider name: {}", provider_name);
let stamp = dnsstamps::DNSCryptBuilder::new(dnsstamps::DNSCryptProvider::new(
provider_name.clone(),
resolver_kp.pk.as_bytes().to_vec(),
provider_kp.pk.as_bytes().to_vec(),
))
.with_addr(listen_addr_s.to_string())
.with_informal_property(InformalProperty::DNSSEC)
@ -335,15 +348,17 @@ fn main() -> Result<(), Error> {
.unwrap();
println!("DNS Stamp: {}", stamp);
let dnscrypt_cert = DNSCryptCert::new(&resolver_kp);
let resolver_kp = CryptKeyPair::new();
let dnscrypt_cert = DNSCryptCert::new(&provider_kp, &resolver_kp);
let dnscrypt_encryption_params = DNSCryptEncryptionParams::new(&provider_kp);
let runtime = Arc::new(Runtime::new()?);
let udp_max_active_connections = 1000;
let tcp_max_active_connections = 100;
let globals = Arc::new(Globals {
runtime: runtime.clone(),
resolver_kp,
dnscrypt_certs: vec![dnscrypt_cert],
dnscrypt_encryption_params_set: vec![dnscrypt_encryption_params],
provider_name,
listen_addr,
upstream_addr,