From dbed0a086720ad276f2c0276a0f1f0dc13c3c397 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 25 Feb 2021 20:04:49 +0100 Subject: [PATCH] Create certificates using a rolling state A new key pair is now computed using the previous secret key as a seed. This still provides forward secrecy, and allows multiple instances to compute the same ephemeral keys without having to share a state. Fixes #57 Fixes #27 --- src/config.rs | 6 ++-- src/crypto.rs | 7 ++-- src/dnscrypt_certs.rs | 80 +++++++++++++++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9aad07f..f3c9776 100644 --- a/src/config.rs +++ b/src/config.rs @@ -112,10 +112,8 @@ pub struct State { impl State { pub fn with_key_pair(provider_kp: SignKeyPair, key_cache_capacity: usize) -> Self { - let dnscrypt_encryption_params_set = vec![DNSCryptEncryptionParams::new( - &provider_kp, - key_cache_capacity, - )]; + let dnscrypt_encryption_params_set = + DNSCryptEncryptionParams::new(&provider_kp, key_cache_capacity, None); State { provider_kp, dnscrypt_encryption_params_set, diff --git a/src/crypto.rs b/src/crypto.rs index cbaad55..1cd8da2 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -132,12 +132,15 @@ pub struct CryptKeyPair { } impl CryptKeyPair { - pub fn new() -> Self { + pub fn from_seed( + seed: [u8; crypto_box_curve25519xchacha20poly1305_SEEDBYTES as usize], + ) -> Self { let mut kp = CryptKeyPair::default(); unsafe { - crypto_box_curve25519xchacha20poly1305_keypair( + crypto_box_curve25519xchacha20poly1305_seed_keypair( kp.pk.0.as_mut_ptr(), kp.sk.0.as_mut_ptr(), + seed.as_ptr(), ) }; kp diff --git a/src/dnscrypt_certs.rs b/src/dnscrypt_certs.rs index bd2320a..ff90c0f 100644 --- a/src/dnscrypt_certs.rs +++ b/src/dnscrypt_certs.rs @@ -7,6 +7,7 @@ use crate::globals::*; use byteorder::{BigEndian, ByteOrder}; use clockpro_cache::ClockProCache; use parking_lot::Mutex; +use rand::prelude::*; use std::mem; use std::slice; use std::sync::Arc; @@ -54,8 +55,7 @@ pub struct DNSCryptCert { } impl DNSCryptCert { - pub fn new(provider_kp: &SignKeyPair, resolver_kp: &CryptKeyPair) -> Self { - let ts_start = now(); + pub fn new(provider_kp: &SignKeyPair, resolver_kp: &CryptKeyPair, ts_start: u32) -> Self { let ts_end = ts_start + DNSCRYPT_CERTS_TTL; let mut dnscrypt_cert = DNSCryptCert::default(); @@ -92,6 +92,10 @@ impl DNSCryptCert { &self.inner.client_magic } + pub fn ts_start(&self) -> u32 { + BigEndian::read_u32(&self.inner.ts_start) + } + pub fn ts_end(&self) -> u32 { BigEndian::read_u32(&self.inner.ts_end) } @@ -108,20 +112,56 @@ pub struct DNSCryptEncryptionParams { } impl DNSCryptEncryptionParams { - pub fn new(provider_kp: &SignKeyPair, cache_capacity: usize) -> Self { - let mut resolver_kp; - while { - resolver_kp = CryptKeyPair::new(); - resolver_kp.pk.as_bytes() + pub fn new( + provider_kp: &SignKeyPair, + cache_capacity: usize, + previous_params: Option>, + ) -> Vec { + let now = now(); + let (mut ts_start, mut seed) = match &previous_params { + None => (now, rand::thread_rng().gen()), + Some(p) => ( + p.dnscrypt_cert().ts_start() + DNSCRYPT_CERTS_RENEWAL, + *p.resolver_kp().sk.as_bytes(), + ), + }; + let mut active_params = vec![]; + loop { + if ts_start > now + DNSCRYPT_CERTS_RENEWAL { + break; + } + let resolver_kp = CryptKeyPair::from_seed(seed); + seed = *resolver_kp.sk.as_bytes(); + if resolver_kp.pk.as_bytes() == &ANONYMIZED_DNSCRYPT_QUERY_MAGIC[..DNSCRYPT_QUERY_MAGIC_SIZE] - } {} - let dnscrypt_cert = DNSCryptCert::new(&provider_kp, &resolver_kp); - let cache = ClockProCache::new(cache_capacity).unwrap(); - DNSCryptEncryptionParams { - dnscrypt_cert, - resolver_kp, - cache: Some(Arc::new(Mutex::new(cache))), + { + ts_start += DNSCRYPT_CERTS_RENEWAL; + continue; + } + if now >= ts_start { + let dnscrypt_cert = DNSCryptCert::new(&provider_kp, &resolver_kp, ts_start); + let cache = ClockProCache::new(cache_capacity).unwrap(); + active_params.push(DNSCryptEncryptionParams { + dnscrypt_cert, + resolver_kp, + cache: Some(Arc::new(Mutex::new(cache))), + }); + } + ts_start += DNSCRYPT_CERTS_RENEWAL; + } + if active_params.is_empty() && previous_params.is_none() { + warn!("Unable to recover a seed; creating an emergency certificate"); + let ts_start = now - (now % DNSCRYPT_CERTS_RENEWAL); + let resolver_kp = CryptKeyPair::from_seed(seed); + let dnscrypt_cert = DNSCryptCert::new(&provider_kp, &resolver_kp, ts_start); + let cache = ClockProCache::new(cache_capacity).unwrap(); + active_params.push(DNSCryptEncryptionParams { + dnscrypt_cert, + resolver_kp, + cache: Some(Arc::new(Mutex::new(cache))), + }); } + active_params } pub fn add_key_cache(&mut self, cache_capacity: usize) { @@ -154,19 +194,23 @@ impl DNSCryptEncryptionParamsUpdater { pub fn update(&self) { let now = now(); let mut new_params_set = vec![]; - { + let previous_params = { let params_set = self.globals.dnscrypt_encryption_params_set.read(); for params in &**params_set { if params.dnscrypt_cert().ts_end() >= now { new_params_set.push(params.clone()); } } - } - let new_params = DNSCryptEncryptionParams::new( + params_set.last().cloned() + }; + let active_params = DNSCryptEncryptionParams::new( &self.globals.provider_kp, self.globals.key_cache_capacity, + previous_params, ); - new_params_set.push(Arc::new(new_params)); + for params in active_params { + new_params_set.push(Arc::new(params)); + } let state = State { provider_kp: self.globals.provider_kp.clone(), dnscrypt_encryption_params_set: new_params_set.iter().map(|x| (**x).clone()).collect(),