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:
parent
58c5452084
commit
5f0bb6daf5
@ -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;
|
||||
|
15
src/dns.rs
15
src/dns.rs
@ -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
71
src/dnscrypt.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
51
src/main.rs
51
src/main.rs
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user