Implement draft 11 packet header

This commit is contained in:
Dirkjan Ochtman
2018-05-01 16:45:57 +02:00
parent a1cc695ac2
commit ce112581a9
6 changed files with 106 additions and 53 deletions
+19 -7
View File
@@ -8,10 +8,10 @@ pub use ring::aead::AES_128_GCM;
pub use ring::digest::SHA256;
pub use ring::hmac::SigningKey;
use types::Side;
use types::{ConnectionId, Side};
pub enum Secret {
Handshake(u64),
Handshake(ConnectionId),
For1Rtt(
&'static aead::Algorithm,
&'static digest::Algorithm,
@@ -20,6 +20,13 @@ pub enum Secret {
}
impl Secret {
pub fn tag_len(&self) -> usize {
match *self {
Secret::Handshake(_) => AES_128_GCM.tag_len(),
Secret::For1Rtt(aead_alg, _, _) => aead_alg.tag_len(),
}
}
pub fn build_key(&self, side: Side) -> PacketKey {
match *self {
Secret::Handshake(cid) => {
@@ -106,7 +113,7 @@ impl PacketKey {
}
}
pub fn expanded_handshake_secret(conn_id: u64, label: &[u8]) -> Vec<u8> {
pub fn expanded_handshake_secret(conn_id: ConnectionId, label: &[u8]) -> Vec<u8> {
let prk = handshake_secret(conn_id);
let mut out = vec![0u8; SHA256.output_len];
qhkdf_expand(&prk, label, &mut out);
@@ -122,10 +129,10 @@ pub fn qhkdf_expand(key: &SigningKey, label: &[u8], out: &mut [u8]) {
hkdf::expand(key, &info, out);
}
fn handshake_secret(conn_id: u64) -> SigningKey {
fn handshake_secret(conn_id: ConnectionId) -> SigningKey {
let key = SigningKey::new(&SHA256, HANDSHAKE_SALT);
let mut buf = Vec::with_capacity(8);
buf.put_u64::<BigEndian>(conn_id);
buf.put_slice(&conn_id);
hkdf::extract(&key, &buf)
}
@@ -134,10 +141,15 @@ const HANDSHAKE_SALT: &[u8; 20] =
#[cfg(test)]
mod tests {
use types::ConnectionId;
#[test]
fn test_handshake_client() {
let conn_id = 0x8394c8f03e515708;
let client_handshake_secret = super::expanded_handshake_secret(conn_id, b"client hs");
let hs_cid = ConnectionId {
len: 8,
bytes: [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
};
let client_handshake_secret = super::expanded_handshake_secret(hs_cid, b"client hs");
let expected = b"\x83\x55\xf2\x1a\x3d\x8f\x83\xec\xb3\xd0\xf9\x71\x08\xd3\xf9\x5e\
\x0f\x65\xb4\xd8\xae\x88\xa0\x61\x1e\xe4\x9d\xb0\xb5\x23\x59\x1d";
assert_eq!(&client_handshake_secret, expected);
+62 -26
View File
@@ -1,8 +1,9 @@
use bytes::{BigEndian, Buf, BufMut};
use codec::{BufLen, Codec};
use codec::{BufLen, Codec, VarLen};
use frame::{Frame, PaddingFrame};
use crypto::PacketKey;
use types::ConnectionId;
use std::io::Cursor;
@@ -17,8 +18,8 @@ impl Packet {
self.header.ptype()
}
pub fn conn_id(&self) -> Option<u64> {
self.header.conn_id()
pub fn dst_cid(&self) -> ConnectionId {
self.header.dst_cid()
}
pub fn number(&self) -> u32 {
@@ -93,8 +94,8 @@ pub struct PartialDecode<'a> {
}
impl<'a> PartialDecode<'a> {
pub fn conn_id(&self) -> Option<u64> {
self.header.conn_id()
pub fn dst_cid(&self) -> ConnectionId {
self.header.dst_cid()
}
pub fn finish(self, key: &PacketKey) -> Packet {
@@ -127,8 +128,10 @@ impl BufLen for Packet {
pub enum Header {
Long {
ptype: LongType,
conn_id: u64,
version: u32,
dst_cid: ConnectionId,
src_cid: ConnectionId,
len: u64,
number: u32,
},
}
@@ -140,9 +143,9 @@ impl Header {
}
}
fn conn_id(&self) -> Option<u64> {
fn dst_cid(&self) -> ConnectionId {
match *self {
Header::Long { conn_id, .. } => Some(conn_id),
Header::Long { dst_cid, .. } => dst_cid,
}
}
@@ -156,7 +159,9 @@ impl Header {
impl BufLen for Header {
fn buf_len(&self) -> usize {
match *self {
Header::Long { .. } => 17,
Header::Long { dst_cid, src_cid, len, .. } => {
10 + (dst_cid.len as usize + src_cid.len as usize) + VarLen(len).buf_len()
}
}
}
}
@@ -166,13 +171,18 @@ impl Codec for Header {
match *self {
Header::Long {
ptype,
conn_id,
version,
dst_cid,
src_cid,
len,
number,
} => {
buf.put_u8(128 | ptype.to_byte());
buf.put_u64::<BigEndian>(conn_id);
buf.put_u32::<BigEndian>(version);
buf.put_u8(dst_cid.len << 4 | src_cid.len);
buf.put_slice(&dst_cid);
buf.put_slice(&src_cid);
VarLen(len).encode(buf);
buf.put_u32::<BigEndian>(number);
}
}
@@ -181,10 +191,24 @@ impl Codec for Header {
fn decode<T: Buf>(buf: &mut T) -> Self {
let first = buf.get_u8();
if first & 128 == 128 {
let version = buf.get_u32::<BigEndian>();
let cils = buf.get_u8();
let (dst_cid, src_cid, used) = {
let (dcil, scil) = ((cils >> 4) as usize, (cils & 15) as usize);
let bytes = buf.bytes();
let dst_cid = ConnectionId::new(&bytes[..dcil]);
let src_cid = ConnectionId::new(&bytes[dcil..dcil + scil]);
(dst_cid, src_cid, dcil + scil)
};
buf.advance(used);
Header::Long {
ptype: LongType::from_byte(first ^ 128),
conn_id: buf.get_u64::<BigEndian>(),
version: buf.get_u32::<BigEndian>(),
version,
dst_cid,
src_cid,
len: VarLen::decode(buf).0,
number: buf.get_u32::<BigEndian>(),
}
} else {
@@ -268,33 +292,45 @@ impl ShortType {
#[cfg(test)]
mod tests {
use super::{Header, LongType, Packet};
use types::{DRAFT_10, Side};
use types::{ConnectionId, DRAFT_11, Side};
use frame::{Frame, StreamFrame};
use crypto::Secret;
use codec::BufLen;
#[test]
fn test_roundtrip() {
let hs_cid = ConnectionId {
len: 9,
bytes: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0],
};
let mut buf = vec![0u8; 65536];
let bytes = b"\x00\x01\x02\x03";
let payload = vec![
Frame::Stream(StreamFrame {
id: 0,
fin: false,
offset: 0,
len: Some(bytes.len() as u64),
data: bytes.to_vec(),
}),
];
let secret = Secret::Handshake(hs_cid);
let len = (payload.buf_len() + secret.tag_len()) as u64;
let packet = Packet {
header: Header::Long {
ptype: LongType::Initial,
conn_id: 123456789,
version: DRAFT_10,
version: DRAFT_11,
dst_cid: hs_cid,
src_cid: hs_cid,
len,
number: 987654321,
},
payload: vec![
Frame::Stream(StreamFrame {
id: 0,
fin: false,
offset: 0,
len: Some(bytes.len() as u64),
data: bytes.to_vec(),
}),
],
payload,
};
let key = Secret::Handshake(123456789).build_key(Side::Client);
let key = secret.build_key(Side::Client);
packet.encode(&key, &mut buf);
let mut decoded = Packet::start_decode(&mut buf).finish(&key);
+6 -7
View File
@@ -2,7 +2,7 @@ use futures::{Future, Poll};
use crypto::Secret;
use packet::{LongType, Packet};
use types::{Endpoint, Side};
use types::{ConnectionId, Endpoint, Side};
use tls::{self, ServerTls};
use std::collections::{HashMap, hash_map::Entry};
@@ -17,7 +17,7 @@ pub struct Server {
tls_config: Arc<tls::ServerConfig>,
in_buf: Vec<u8>,
out_buf: Vec<u8>,
connections: HashMap<u64, (SocketAddr, Endpoint<ServerTls>)>,
connections: HashMap<ConnectionId, (SocketAddr, Endpoint<ServerTls>)>,
}
impl Server {
@@ -45,16 +45,16 @@ impl Future for Server {
loop {
let (len, addr) = try_ready!(self.socket.poll_recv_from(&mut self.in_buf));
let partial = Packet::start_decode(&mut self.in_buf[..len]);
let conn_id = partial.conn_id().unwrap();
match self.connections.entry(conn_id) {
let dst_cid = partial.dst_cid();
match self.connections.entry(dst_cid) {
Entry::Occupied(_) => {
println!("connection found for {}", conn_id);
println!("connection found for {:?}", dst_cid);
}
Entry::Vacant(entry) => {
let endpoint = Endpoint::new(
ServerTls::with_config(&self.tls_config),
Side::Server,
Some(Secret::Handshake(conn_id))
Some(Secret::Handshake(dst_cid))
);
let &mut (addr, ref mut endpoint) = entry.insert((addr, endpoint));
let key = endpoint.decode_key(&partial.header);
@@ -75,4 +75,3 @@ impl Future for Server {
}
}
}
+3 -3
View File
@@ -7,7 +7,7 @@ use std::sync::Arc;
use crypto::Secret;
use tls::{ClientTls, ServerTls};
use types::{Endpoint, Side};
use types::{ConnectionId, Endpoint, Side};
use self::untrusted::Input;
@@ -18,12 +18,12 @@ fn test_handshake() {
let mut c = client_endpoint();
let initial = c.initial("example.com");
let mut s = server_endpoint(initial.conn_id().unwrap());
let mut s = server_endpoint(initial.dst_cid());
let server_hello = s.handle_handshake(&initial).unwrap();
assert!(c.handle_handshake(&server_hello).is_some());
}
fn server_endpoint(hs_cid: u64) -> Endpoint<ServerTls> {
fn server_endpoint(hs_cid: ConnectionId) -> Endpoint<ServerTls> {
let certs = {
let f = File::open("certs/server.chain").expect("cannot open 'certs/server.chain'");
let mut reader = BufReader::new(f);
+3 -3
View File
@@ -7,7 +7,7 @@ use rustls::quic::{ClientSession, QuicSecret, ServerSession, TLSResult};
use std::sync::Arc;
use crypto::Secret;
use types::{DRAFT_10, TransportParameter};
use types::{DRAFT_11, TransportParameter};
use webpki::{DNSNameRef, TLSServerTrustAnchors};
use webpki_roots;
@@ -71,8 +71,8 @@ impl ServerTls {
session: ServerSession::new(
config,
ServerTransportParameters {
negotiated_version: DRAFT_10,
supported_versions: vec![DRAFT_10],
negotiated_version: DRAFT_11,
supported_versions: vec![DRAFT_11],
parameters: encode_transport_parameters(&vec![
TransportParameter::InitialMaxStreamData(131072),
TransportParameter::InitialMaxData(1048576),
+13 -7
View File
@@ -11,7 +11,8 @@ use tls::{ClientTls, QuicTls};
pub struct Endpoint<T> {
side: Side,
pub dst_cid: u64,
pub dst_cid: ConnectionId,
pub src_cid: ConnectionId,
pub src_pn: u32,
secret: Secret,
prev_secret: Option<Secret>,
@@ -39,6 +40,7 @@ where
tls,
side,
dst_cid,
src_cid: rng.gen(),
src_pn: rng.gen(),
secret,
prev_secret: None,
@@ -69,8 +71,10 @@ where
Packet {
header: Header::Long {
ptype: LongType::Initial,
conn_id: self.dst_cid,
version: DRAFT_10,
version: DRAFT_11,
dst_cid: self.dst_cid,
src_cid: self.src_cid,
len: (payload.buf_len() + self.secret.tag_len()) as u64,
number,
},
payload,
@@ -83,8 +87,10 @@ where
Packet {
header: Header::Long {
ptype: LongType::Handshake,
conn_id: self.dst_cid,
version: DRAFT_10,
version: DRAFT_11,
dst_cid: self.dst_cid,
src_cid: self.src_cid,
len: (payload.buf_len() + self.secret.tag_len()) as u64,
number,
},
payload,
@@ -92,7 +98,7 @@ where
}
pub(crate) fn handle_handshake(&mut self, rsp: &Packet) -> Option<Packet> {
self.dst_cid = rsp.conn_id().unwrap();
self.dst_cid = rsp.dst_cid();
let tls_frame = rsp.payload
.iter()
.filter_map(|f| match *f {
@@ -203,4 +209,4 @@ pub enum Side {
impl Copy for Side {}
pub const DRAFT_10: u32 = 0xff00000a;
pub const DRAFT_11: u32 = 0xff00000b;