Skip to content

Commit

Permalink
[#783] Encrypt retry token
Browse files Browse the repository at this point in the history
  • Loading branch information
kansi committed Sep 20, 2020
1 parent c0eaab7 commit 964c005
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 55 deletions.
27 changes: 13 additions & 14 deletions quinn-proto/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rand::RngCore;
use crate::crypto::types::{Certificate, CertificateChain, PrivateKey};
use crate::{
congestion,
crypto::{self, ClientConfig as _, HmacKey as _, ServerConfig as _},
crypto::{self, ClientConfig as _, HmacKey as _, PseudoRandomKey as _, ServerConfig as _},
VarInt, MAX_CID_SIZE,
};

Expand Down Expand Up @@ -402,8 +402,9 @@ where
/// Must be set to use TLS 1.3 only.
pub crypto: S::ServerConfig,

/// Private key used to authenticate data included in handshake tokens.
pub(crate) token_key: Arc<S::HmacKey>,
/// Used to generate one-time AEAD keys to protect handshake tokens
pub(crate) token_key: Arc<S::PseudoRandomKey>,

/// Whether to require clients to prove ownership of an address before committing resources.
///
/// Introduces an additional round-trip to the handshake to make denial of service attacks more difficult.
Expand All @@ -427,13 +428,13 @@ impl<S> ServerConfig<S>
where
S: crypto::Session,
{
/// Create a default config with a particular `token_key`
pub fn new(token_key: S::HmacKey) -> Self {
/// Create a default config with a particular `master_key`
pub fn new(prk: S::PseudoRandomKey) -> Self {
Self {
transport: Arc::new(TransportConfig::default()),
crypto: S::ServerConfig::new(),

token_key: Arc::new(token_key),
token_key: Arc::new(prk),
use_stateless_retry: false,
retry_token_lifetime: 15_000_000,

Expand All @@ -444,8 +445,8 @@ where
}

/// Private key used to authenticate data included in handshake tokens.
pub fn token_key(&mut self, value: &[u8]) -> Result<&mut Self, ConfigError> {
self.token_key = Arc::new(S::HmacKey::new(value)?);
pub fn token_key(&mut self, master_key: &[u8]) -> Result<&mut Self, ConfigError> {
self.token_key = Arc::new(S::PseudoRandomKey::from_secret(&master_key));
Ok(self)
}

Expand Down Expand Up @@ -518,12 +519,10 @@ where
fn default() -> Self {
let rng = &mut rand::thread_rng();

let mut token_key = vec![0; S::HmacKey::KEY_LEN];
rng.fill_bytes(&mut token_key);
Self::new(
S::HmacKey::new(&token_key)
.expect("HMAC key rejected random bytes; use ServerConfig::new instead"),
)
let mut master_key = [0u8; 64];
rng.fill_bytes(&mut master_key);

Self::new(S::PseudoRandomKey::from_secret(&master_key))
}
}

Expand Down
27 changes: 27 additions & 0 deletions quinn-proto/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ pub trait Session: Send + Sized {
type ClientConfig: ClientConfig<Self>;
/// Type used to sign various values
type HmacKey: HmacKey;
/// Pseudo random key used for generating AEAD key
type PseudoRandomKey: PseudoRandomKey;
/// Key used for protecting handshake tokens
type AeadKey: AeadKey;
/// Type of keys used to protect packet headers
type HeaderKey: HeaderKey;
/// Type used to represent packet protection keys
Expand Down Expand Up @@ -186,3 +190,26 @@ pub trait HmacKey: Send + Sized + Sync {
/// Method for verifying a message
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), ()>;
}

/// A pseudo random key for HKDF
pub trait PseudoRandomKey: Send + Sized + Sync {
/// AEAD key type
type AeadKey: AeadKey;

/// Derive AEAD using hkdf
fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Self::AeadKey;
/// Method to build pseudo random key from existing bytes
fn from_secret(secret: &[u8]) -> Self;
}

/// A key for sealing data with AEAD-based algorithms
pub trait AeadKey {
/// Length of AEAD Key
const KEY_LEN: usize;

// fn from_hkdf(master_key: &impl PseudoRandomKey, random_bytes: &[u8]) -> Self;
/// Method for sealing message `data`
fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), ()>;
/// Method for opening a sealed message `data`
fn open<'a>(&self, data: &'a mut [u8], additional_data: &[u8]) -> Result<&'a mut [u8], ()>;
}
38 changes: 37 additions & 1 deletion quinn-proto/src/crypto/ring.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ring::{aead, hmac};
use ring::{aead, hkdf, hmac};

use crate::{
config::ConfigError,
Expand Down Expand Up @@ -70,3 +70,39 @@ impl crypto::HmacKey for hmac::Key {
hmac::verify(self, data, signature).map_err(|_| ())
}
}

impl crypto::PseudoRandomKey for hkdf::Prk {
type AeadKey = ring::aead::LessSafeKey;

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Self::AeadKey {
let mut key_buffer = [0u8; 32];
let info = [random_bytes];
let okm = self.expand(&info, hkdf::HKDF_SHA256).unwrap();

okm.fill(&mut key_buffer).unwrap();

let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_buffer).unwrap();
Self::AeadKey::new(key)
}

fn from_secret(bytes: &[u8]) -> Self {
hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(bytes)
}
}

impl crypto::AeadKey for aead::LessSafeKey {
const KEY_LEN: usize = 32;

fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), ()> {
let aad = ring::aead::Aad::from(additional_data);
let zero_nonce = ring::aead::Nonce::assume_unique_for_key([0u8; 12]);
self.seal_in_place_append_tag(zero_nonce, aad, data)
.map_err(|_| ())
}

fn open<'a>(&self, data: &'a mut [u8], additional_data: &[u8]) -> Result<&'a mut [u8], ()> {
let aad = ring::aead::Aad::from(additional_data);
let zero_nonce = ring::aead::Nonce::assume_unique_for_key([0u8; 12]);
self.open_in_place(zero_nonce, aad, data).map_err(|_| ())
}
}
4 changes: 3 additions & 1 deletion quinn-proto/src/crypto/rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
};

use bytes::BytesMut;
use ring::{aead, aead::quic::HeaderProtectionKey, hmac};
use ring::{aead, aead::quic::HeaderProtectionKey, hkdf, hmac};
pub use rustls::TLSError;
use rustls::{
self,
Expand Down Expand Up @@ -50,6 +50,8 @@ impl crypto::Session for TlsSession {
type Identity = CertificateChain;
type ClientConfig = Arc<rustls::ClientConfig>;
type HmacKey = hmac::Key;
type PseudoRandomKey = hkdf::Prk;
type AeadKey = aead::LessSafeKey;
type PacketKey = PacketKey;
type HeaderKey = HeaderProtectionKey;
type ServerConfig = Arc<rustls::ServerConfig>;
Expand Down
7 changes: 6 additions & 1 deletion quinn-proto/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,16 +537,21 @@ where
let (retry_src_cid, orig_dst_cid) = if server_config.use_stateless_retry {
if token.is_empty() {
// First Initial
let mut random_bytes = vec![0u8; RetryToken::RANDOM_BYTES_LEN];
self.rng.fill_bytes(&mut random_bytes);

let token = RetryToken {
orig_dst_cid: dst_cid,
issued: SystemTime::now(),
random_bytes: &random_bytes,
}
.encode(&*server_config.token_key, &remote, &temp_loc_cid);
let mut buf = Vec::new();

let header = Header::Retry {
src_cid: temp_loc_cid,
dst_cid: src_cid,
};
let mut buf = Vec::new();
let encode = header.encode(&mut buf);
buf.put_slice(&token);
buf.extend_from_slice(&S::retry_tag(&dst_cid, &buf));
Expand Down
1 change: 1 addition & 0 deletions quinn-proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,4 @@ const MIN_MTU: u16 = 1232;
const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
/// Maximum number of streams that can be uniquely identified by a stream ID
const MAX_STREAM_COUNT: u64 = 1 << 60;
const MAX_ADDITIONAL_DATA_SIZE: usize = 39; // max(ipv4, ipv6) + port + retry_src_cid
Loading

0 comments on commit 964c005

Please sign in to comment.