Bug 1772092 - Neqo version 0.6.1 r=necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D156603
This commit is contained in:
Dragana Damjanovic 2022-09-07 10:10:27 +00:00
Родитель ddf51b273c
Коммит f29ba14bfb
97 изменённых файлов: 2809 добавлений и 1016 удалений

Просмотреть файл

@ -20,7 +20,7 @@ rev = "bb2039f077a29dba0879372a67e764e6ace8e33f"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"
replace-with = "vendored-sources"
tag = "v0.5.7"
tag = "v0.6.1"
[source."https://github.com/mozilla/mp4parse-rust"]
git = "https://github.com/mozilla/mp4parse-rust"

59
Cargo.lock сгенерированный
Просмотреть файл

@ -191,7 +191,7 @@ dependencies = [
"quote",
"serde",
"syn",
"toml 0.5.9",
"toml",
]
[[package]]
@ -418,13 +418,6 @@ dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.56.999"
dependencies = [
"bindgen 0.59.2",
]
[[package]]
name = "bindgen"
version = "0.59.2"
@ -901,7 +894,7 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6"
dependencies = [
"bindgen 0.59.2",
"bindgen",
]
[[package]]
@ -1988,7 +1981,7 @@ name = "gecko-profiler"
version = "0.1.0"
dependencies = [
"bincode",
"bindgen 0.59.2",
"bindgen",
"lazy_static",
"mozbuild",
"profiler-macros",
@ -2515,7 +2508,7 @@ name = "http3server"
version = "0.1.1"
dependencies = [
"base64",
"bindgen 0.59.2",
"bindgen",
"log",
"mio 0.6.23",
"mio-extras",
@ -3446,7 +3439,7 @@ dependencies = [
name = "mozilla-central-workspace-hack"
version = "0.1.0"
dependencies = [
"bindgen 0.59.2",
"bindgen",
"libc",
"quote",
"serde",
@ -3565,10 +3558,11 @@ dependencies = [
[[package]]
name = "neqo-common"
version = "0.5.7"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
dependencies = [
"chrono",
"env_logger 0.8.999",
"env_logger 0.9.0",
"lazy_static",
"log",
"qlog",
@ -3577,22 +3571,22 @@ dependencies = [
[[package]]
name = "neqo-crypto"
version = "0.5.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.5.7#f3de275b12c40f45718ce43a0482e771ba6cd4b8"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
dependencies = [
"bindgen 0.56.999",
"bindgen",
"log",
"mozbuild",
"neqo-common",
"serde",
"serde_derive",
"toml 0.4.999",
"toml",
]
[[package]]
name = "neqo-http3"
version = "0.5.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.5.7#f3de275b12c40f45718ce43a0482e771ba6cd4b8"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
dependencies = [
"enumset",
"lazy_static",
@ -3609,8 +3603,8 @@ dependencies = [
[[package]]
name = "neqo-qpack"
version = "0.5.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.5.7#f3de275b12c40f45718ce43a0482e771ba6cd4b8"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
dependencies = [
"lazy_static",
"log",
@ -3623,8 +3617,8 @@ dependencies = [
[[package]]
name = "neqo-transport"
version = "0.5.7"
source = "git+https://github.com/mozilla/neqo?tag=v0.5.7#f3de275b12c40f45718ce43a0482e771ba6cd4b8"
version = "0.6.1"
source = "git+https://github.com/mozilla/neqo?tag=v0.6.1#e71e860489447fed25b64bd8ec31a804dccb0dad"
dependencies = [
"indexmap",
"lazy_static",
@ -4074,7 +4068,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "054f65db435bb7cf99db2f38bbcf568dfa66c342201df54e700c772b178e2f20"
dependencies = [
"bindgen 0.59.2",
"bindgen",
]
[[package]]
@ -4957,7 +4951,7 @@ dependencies = [
"app_units",
"arrayvec",
"atomic_refcell",
"bindgen 0.59.2",
"bindgen",
"bitflags",
"byteorder",
"cssparser",
@ -4998,7 +4992,7 @@ dependencies = [
"time 0.1.44",
"to_shmem",
"to_shmem_derive",
"toml 0.5.9",
"toml",
"uluru",
"unicode-bidi",
"unicode-segmentation",
@ -5409,13 +5403,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.4.999"
dependencies = [
"toml 0.5.9",
]
[[package]]
name = "toml"
version = "0.5.9"
@ -5645,7 +5632,7 @@ dependencies = [
"extend",
"heck",
"serde",
"toml 0.5.9",
"toml",
"uniffi_bindgen",
]
@ -5731,7 +5718,7 @@ dependencies = [
"lazy_static",
"paste",
"serde",
"toml 0.5.9",
"toml",
"weedle2",
]

Просмотреть файл

@ -91,9 +91,6 @@ vcpkg = { path = "build/rust/vcpkg" }
# Helper crate for integration in the gecko build system.
mozbuild = { path = "build/rust/mozbuild" }
# Patch bindgen 0.56 to 0.59.
bindgen = { path = "build/rust/bindgen" }
# Patch cfg-if 0.1 to 1.0
cfg-if = { path = "build/rust/cfg-if" }
@ -112,9 +109,6 @@ tokio-util = { path = "build/rust/tokio-util" }
# Patch env_logger 0.8 to 0.9
env_logger = { path = "build/rust/env_logger" }
# Patch toml 0.4 to 0.5
toml = { path = "build/rust/toml" }
# Patch parking_lot 0.12 down to 0.11, which is compatible for most crates that use it, to avoid
# dependencies on windows-sys.
parking_lot = { path = "build/rust/parking_lot" }
@ -164,11 +158,6 @@ webext-storage = { git = "https://github.com/mozilla/application-services", rev
[patch.crates-io.mio]
path = "third_party/rust/mio-0.6.23"
# Patch neqo 0.5.7 to be compatible with newer versions of the log crate.
# https://github.com/mozilla/neqo/pull/1350
[patch."https://github.com/mozilla/neqo"]
neqo-common = { path = "third_party/rust/neqo-common" }
# These are used to test UniFFI functionality. We haven't figured out how we
# want to publish these yet, so they are only accessible via git. This works
# okay, but it means that their dependencies on UniFFI crates will normally

Просмотреть файл

@ -1,19 +0,0 @@
[package]
name = "bindgen"
version = "0.56.999"
edition = "2018"
license = "BSD-3-Clause"
[lib]
path = "lib.rs"
[dependencies.bindgen]
version = "0.59"
default-features = false
[features]
default = ["bindgen/default"]
logging = ["bindgen/logging"]
runtime = ["bindgen/runtime"]
static = ["bindgen/static"]
which-rustfmt = ["bindgen/which-rustfmt"]

Просмотреть файл

@ -1,26 +0,0 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
pub use bindgen::*;

Просмотреть файл

@ -1,11 +0,0 @@
[package]
name = "toml"
version = "0.4.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies]
toml = "0.5"

Просмотреть файл

@ -1,5 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub use toml::*;

Просмотреть файл

@ -8,10 +8,10 @@ edition = "2018"
name = "neqo_glue"
[dependencies]
neqo-http3 = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
xpcom = { path = "../../../xpcom/rust/xpcom" }
@ -25,7 +25,7 @@ static_prefs = { path = "../../../modules/libpref/init/static_prefs", optional =
winapi = {version = "0.3", features = ["ws2def"] }
[dependencies.neqo-crypto]
tag = "v0.5.7"
tag = "v0.6.1"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

Просмотреть файл

@ -5,17 +5,17 @@ authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
[dependencies]
neqo-transport = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.5.7", git = "https://github.com/mozilla/neqo" }
neqo-transport = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-common = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-http3 = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
neqo-qpack = { tag = "v0.6.1", git = "https://github.com/mozilla/neqo" }
mio = "0.6.17"
mio-extras = "2.0.5"
log = "0.4.0"
base64 = "0.13"
[dependencies.neqo-crypto]
tag = "v0.5.7"
tag = "v0.6.1"
git = "https://github.com/mozilla/neqo"
default-features = false
features = ["gecko"]

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"9013a62945e20404cfc3624df017feeb3b86e096b6882b0fd2254c4e87d24b6b","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"876fe7da558964046765aa2a2d7ebad9d53e1d4b31a1bf233d47b939f417dba1","src/datagram.rs":"569f8d9e34d7ee17144bf63d34136ecd9778da0d337e513f338738c50284615e","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"45a608ce9f00e2666ce95422a278c6dc0ff4e229b114e7bcf0b4c0d9dc61ad56","src/incrdecoder.rs":"97eb93502afabf13d46de37ca05430c49e876bfa9f013ce264231639eaf9df64","src/lib.rs":"0a3679ab0bc67817097701010881e1c2f48ad1ab0700f12babc46cc59c5c788b","src/log.rs":"b69e492af85e65866cb6588138e8a337dd897d3ce399cb4e9fb8cc04ac042b7f","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}
{"files":{"Cargo.toml":"1e35362eb965155414173a29d9f0fbe1af3c6a66aa158af6701f235688dacd5c","build.rs":"a17b1bb1bd3de3fc958f72d4d1357f7bc4432faa26640c95b5fbfccf40579d67","src/codec.rs":"5aeb997fd0eca77241d1cc173f0d37aa8d2ec1725d5e3739c28f1999bf7c4bed","src/datagram.rs":"742aa0f39ac24d63431b58e23ebf925e27ec42340e5911020475de5f7f457a6d","src/event.rs":"f60fee9f4b09ef47ff5e4bfa21c07e45ffd5873c292f2605f24d834070127d62","src/header.rs":"b7d4eeb40952b36f71ae1f37ce82c9617af8b84c171576de4eca9d50a3071103","src/hrtime.rs":"c5f9f65a642b5782589a4caaf1e758845daa7ebf56c58dc9ef9f836e48026d29","src/incrdecoder.rs":"458f0228e41018d58f5a83c7895c9ea283f310fcb91c969e22026eb8ca3c84c9","src/lib.rs":"bf3e9922196f09554ef55d24c3331bb4c249d583fbbd9f5815670d9d23160838","src/log.rs":"f1ba46a9c2ef10b27211561f4851f933db941ec85ccd3425376d060034aea051","src/qlog.rs":"ca323c91d61810ebef2ebeb967836dda384a60a9fb492c2b8d1b235a98f2e4bf","src/timer.rs":"e63af7e7df968bf702583f263cfb63e6dca4e599bacffa2de0a6383d85333636","tests/log.rs":"480b165b7907ec642c508b303d63005eee1427115d6973a349eaf6b2242ed18d"},"package":null}

5
third_party/rust/neqo-common/Cargo.toml поставляемый
Просмотреть файл

@ -1,14 +1,15 @@
[package]
name = "neqo-common"
version = "0.5.7"
version = "0.6.1"
authors = ["Bobby Holley <bobbyholley@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"
license = "MIT/Apache-2.0"
build = "build.rs"
[dependencies]
log = {version = "0.4.0", default-features = false}
env_logger = {version = "0.8", default-features = false}
env_logger = {version = "0.9", default-features = false}
lazy_static = "1.3.0"
qlog = "0.4.0"
chrono = "0.4.10"

64
third_party/rust/neqo-common/src/codec.rs поставляемый
Просмотреть файл

@ -6,7 +6,6 @@
use std::convert::TryFrom;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use crate::hex_with_len;
@ -159,18 +158,18 @@ impl<'a> Decoder<'a> {
}
}
// Implement `Deref` for `Decoder` so that values can be examined without moving the cursor.
impl<'a> Deref for Decoder<'a> {
type Target = [u8];
// Implement `AsRef` for `Decoder` so that values can be examined without
// moving the cursor.
impl<'a> AsRef<[u8]> for Decoder<'a> {
#[must_use]
fn deref(&self) -> &[u8] {
fn as_ref(&self) -> &'a [u8] {
&self.buf[self.offset..]
}
}
impl<'a> Debug for Decoder<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(&hex_with_len(&self[..]))
f.write_str(&hex_with_len(self.as_ref()))
}
}
@ -199,7 +198,7 @@ impl<'a, 'b> PartialEq<Decoder<'b>> for Decoder<'a> {
}
/// Encoder is good for building data structures.
#[derive(Clone, Default, PartialEq)]
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Encoder {
buf: Vec<u8>,
}
@ -248,11 +247,24 @@ impl Encoder {
self.buf.capacity()
}
/// Get the length of the underlying buffer: the number of bytes that have
/// been written to the buffer.
#[must_use]
pub fn len(&self) -> usize {
self.buf.len()
}
/// Returns true if the encoder buffer contains no elements.
#[must_use]
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
/// Create a view of the current contents of the buffer.
/// Note: for a view of a slice, use `Decoder::new(&enc[s..e])`
#[must_use]
pub fn as_decoder(&self) -> Decoder {
Decoder::new(self)
Decoder::new(self.as_ref())
}
/// Don't use this except in testing.
@ -275,7 +287,7 @@ impl Encoder {
/// Generic encode routine for arbitrary data.
pub fn encode(&mut self, data: &[u8]) -> &mut Self {
self.buf.extend_from_slice(data);
self.buf.extend_from_slice(data.as_ref());
self
}
@ -317,7 +329,7 @@ impl Encoder {
/// # Panics
/// When `v` is longer than 2^64.
pub fn encode_vec(&mut self, n: usize, v: &[u8]) -> &mut Self {
self.encode_uint(n, u64::try_from(v.len()).unwrap())
self.encode_uint(n, u64::try_from(v.as_ref().len()).unwrap())
.encode(v)
}
@ -341,7 +353,7 @@ impl Encoder {
/// # Panics
/// When `v` is longer than 2^64.
pub fn encode_vvec(&mut self, v: &[u8]) -> &mut Self {
self.encode_varint(u64::try_from(v.len()).unwrap())
self.encode_varint(u64::try_from(v.as_ref().len()).unwrap())
.encode(v)
}
@ -411,6 +423,12 @@ impl AsRef<[u8]> for Encoder {
}
}
impl AsMut<[u8]> for Encoder {
fn as_mut(&mut self) -> &mut [u8] {
self.buf.as_mut()
}
}
impl<'a> From<Decoder<'a>> for Encoder {
#[must_use]
fn from(dec: Decoder<'a>) -> Self {
@ -434,20 +452,6 @@ impl From<Encoder> for Vec<u8> {
}
}
impl Deref for Encoder {
type Target = [u8];
#[must_use]
fn deref(&self) -> &[u8] {
&self.buf[..]
}
}
impl DerefMut for Encoder {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.buf[..]
}
}
#[cfg(test)]
mod tests {
use super::{Decoder, Encoder};
@ -749,7 +753,7 @@ mod tests {
fn encode_vec_with() {
let mut enc = Encoder::default();
enc.encode_vec_with(2, |enc_inner| {
enc_inner.encode(&Encoder::from_hex("02"));
enc_inner.encode(Encoder::from_hex("02").as_ref());
});
assert_eq!(enc, Encoder::from_hex("000102"));
}
@ -774,7 +778,7 @@ mod tests {
fn encode_vvec_with() {
let mut enc = Encoder::default();
enc.encode_vvec_with(|enc_inner| {
enc_inner.encode(&Encoder::from_hex("02"));
enc_inner.encode(Encoder::from_hex("02").as_ref());
});
assert_eq!(enc, Encoder::from_hex("0102"));
}
@ -794,7 +798,7 @@ mod tests {
fn encode_builder() {
let mut enc = Encoder::from_hex("ff");
let enc2 = Encoder::from_hex("010234");
enc.encode(&enc2);
enc.encode(enc2.as_ref());
assert_eq!(enc, Encoder::from_hex("ff010234"));
}
@ -804,14 +808,14 @@ mod tests {
let mut enc = Encoder::from_hex("ff");
let enc2 = Encoder::from_hex("010234");
let v = enc2.as_decoder();
enc.encode(&v);
enc.encode(v.as_ref());
assert_eq!(enc, Encoder::from_hex("ff010234"));
}
#[test]
fn encode_mutate() {
let mut enc = Encoder::from_hex("010234");
enc[0] = 0xff;
enc.as_mut()[0] = 0xff;
assert_eq!(enc, Encoder::from_hex("ff0234"));
}

Просмотреть файл

@ -9,7 +9,7 @@ use std::ops::Deref;
use crate::hex_with_len;
#[derive(PartialEq, Clone)]
#[derive(PartialEq, Eq, Clone)]
pub struct Datagram {
src: SocketAddr,
dst: SocketAddr,

5
third_party/rust/neqo-common/src/hrtime.rs поставляемый
Просмотреть файл

@ -82,6 +82,7 @@ impl PeriodSet {
#[allow(non_camel_case_types)]
mod mac {
use std::mem::size_of;
use std::ptr::addr_of_mut;
// These are manually extracted from the many bindings generated
// by bindgen when provided with the simple header:
@ -162,7 +163,7 @@ mod mac {
thread_policy_set(
pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
&mut policy as *mut thread_time_constraint_policy as *mut _,
addr_of_mut!(policy) as _, // horror!
THREAD_TIME_CONSTRAINT_POLICY_COUNT,
)
};
@ -197,7 +198,7 @@ mod mac {
thread_policy_get(
pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
&mut policy as *mut thread_time_constraint_policy as *mut _,
addr_of_mut!(policy) as _, // horror!
&mut count,
&mut get_default,
)

Просмотреть файл

@ -178,7 +178,7 @@ mod tests {
for tail in 1..db.len() {
let split = db.len() - tail;
let mut dv = Decoder::from(&db[0..split]);
let mut dv = Decoder::from(&db.as_ref()[0..split]);
eprintln!(" split at {}: {:?}", split, dv);
// Clone the basic decoder for each iteration of the loop.
@ -192,7 +192,7 @@ mod tests {
if tail > 1 {
assert_eq!(res, None);
assert!(dec.min_remaining() > 0);
let mut dv = Decoder::from(&db[split..]);
let mut dv = Decoder::from(&db.as_ref()[split..]);
eprintln!(" split remainder {}: {:?}", split, dv);
res = dec.consume(&mut dv);
assert_eq!(dv.remaining(), 1);
@ -230,7 +230,7 @@ mod tests {
#[test]
fn zero_len() {
let enc = Encoder::from_hex("ff");
let mut dec = Decoder::new(&enc);
let mut dec = Decoder::new(enc.as_ref());
let mut incr = IncrementalDecoderBuffer::new(0);
assert_eq!(incr.consume(&mut dec), Some(Vec::new()));
assert_eq!(dec.remaining(), enc.len());
@ -244,7 +244,7 @@ mod tests {
for tail in 1..db.len() {
let split = db.len() - tail;
let mut dv = Decoder::from(&db[0..split]);
let mut dv = Decoder::from(&db.as_ref()[0..split]);
eprintln!(" split at {}: {:?}", split, dv);
// Clone the basic decoder for each iteration of the loop.
@ -256,7 +256,7 @@ mod tests {
if tail > 1 {
assert!(!res);
assert!(dec.min_remaining() > 0);
let mut dv = Decoder::from(&db[split..]);
let mut dv = Decoder::from(&db.as_ref()[split..]);
eprintln!(" split remainder {}: {:?}", split, dv);
res = dec.consume(&mut dv);
assert_eq!(dv.remaining(), 1);

16
third_party/rust/neqo-common/src/lib.rs поставляемый
Просмотреть файл

@ -27,11 +27,13 @@ pub use self::incrdecoder::{
#[macro_use]
extern crate lazy_static;
use std::fmt::Write;
#[must_use]
pub fn hex(buf: impl AsRef<[u8]>) -> String {
let mut ret = String::with_capacity(buf.as_ref().len() * 2);
for b in buf.as_ref() {
ret.push_str(&format!("{:02x}", b));
write!(&mut ret, "{:02x}", b).unwrap();
}
ret
}
@ -44,13 +46,13 @@ pub fn hex_snip_middle(buf: impl AsRef<[u8]>) -> String {
hex_with_len(buf)
} else {
let mut ret = String::with_capacity(SHOW_LEN * 2 + 16);
ret.push_str(&format!("[{}]: ", buf.len()));
write!(&mut ret, "[{}]: ", buf.len()).unwrap();
for b in &buf[..SHOW_LEN] {
ret.push_str(&format!("{:02x}", b));
write!(&mut ret, "{:02x}", b).unwrap();
}
ret.push_str("..");
for b in &buf[buf.len() - SHOW_LEN..] {
ret.push_str(&format!("{:02x}", b));
write!(&mut ret, "{:02x}", b).unwrap();
}
ret
}
@ -60,9 +62,9 @@ pub fn hex_snip_middle(buf: impl AsRef<[u8]>) -> String {
pub fn hex_with_len(buf: impl AsRef<[u8]>) -> String {
let buf = buf.as_ref();
let mut ret = String::with_capacity(10 + buf.len() * 2);
ret.push_str(&format!("[{}]: ", buf.len()));
write!(&mut ret, "[{}]: ", buf.len()).unwrap();
for b in buf {
ret.push_str(&format!("{:02x}", b));
write!(&mut ret, "{:02x}", b).unwrap();
}
ret
}
@ -76,7 +78,7 @@ pub const fn const_min(a: usize, b: usize) -> usize {
[a, b][(a >= b) as usize]
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
/// Client or Server.
pub enum Role {
Client,

2
third_party/rust/neqo-common/src/log.rs поставляемый
Просмотреть файл

@ -4,6 +4,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::module_name_repetitions)]
use std::io::Write;
use std::sync::Once;
use std::time::Instant;

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"561d34c14380159b08a88a2461a31355e63fbf410725dd28a12bbb3769596675","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"d136f82a333b0ee1499e7858fdfc3d630f7ff37501a3c51028a4eeb7e2f136b4","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"659943cfae3c65be50e92d4b5b499c4580b4c522e9905ba567b434c2081784bc","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"4e60d5a2ee6dedfd08602fa36318239e731244825df2cb801ca1d88f5f2a41c1","src/agent.rs":"5caad7dfe81b0afbadb4c12e46634961880d12daa4820d9adcfa84c240a36ac2","src/agentio.rs":"bce4c3dfcfa433209a409ac0c0752f8c95ab37bb6239a42f99b83858e8747bd1","src/auth.rs":"e821dac1511691151a6e64b7c7130a07d941dffad4529b2631f20ddd07d3f20c","src/cert.rs":"04d7328ab59a5268f2f48b3f880192bf28d42c09c362ef5906ee66e087c754d1","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"1b6ee298855d34310a0d65367b21fdc38678a9c37fc7e1d9579c3c8dfd753377","src/err.rs":"d4dbe63e2faba3a1f08dca015549c32550cb18907592abc3831e05e330f0a93b","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"46a2023c421d89fda8d09b356b648272857fd20ee5cf5829143ac88402b32e4b","src/lib.rs":"db8cbe315dbfd32c187d63000b15a2fc758104b844714a96e47330bbf746be57","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"ae054861719fdead8227220dd5a28b92882756683a436676470b672ee26b9a4e","src/prio.rs":"4224a65f25d7de9bf7d6cb18b15597a39650b3c4fcf7d184a4e4bd7f65cebccd","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"48790a330994d892742048000bd12460b7eee2c3daaa444481b8527406d0a4c7","src/selfencrypt.rs":"4a9af42ccefbc77c65baedf00ef389de4fa7ed855d7ab3b60542b5931050667d","src/ssl.rs":"32e934e6dc5df4e4b4cbe96bae53921cf09a684959cb5ad3469cd65965f3164c","src/time.rs":"ddecb9f6cb6b3367852943d27fc89fd36d3c0ca0c9b5c9797494b74de2d8b5c7","tests/aead.rs":"a0fe826aa3bfcce22dbe1b06b74823cb2334331ffe6ce6152952613e9e1ccae5","tests/agent.rs":"94819f9eeba2afa0c25adc821755900f1488fd47af6d84d9507a112c29d1752a","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"0fcfa8958686aacb42c56c51c6b234842fe990470d2069a67509869baaa18452","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"92e062538c01fa7a474225714ed238d846ceb8c8feb9d79eb05be6111b00fb1e","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}
{"files":{"Cargo.toml":"372b8108f43f8df1f6f26e72e434a2ae0ba00b8b4dba193451eb427738734069","TODO":"ac0f1c2ebcca03f5b3c0cc56c5aedbb030a4b511e438bc07a57361c789f91e9f","bindings/bindings.toml":"cb3e29ac6d1fd7083ffab81494afe1b9a2d9e41c60774438fd9681974c87c252","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"99df86d0c11da5a8cbcc3be2560e6d764234a21b5d9b5f7ab1b70696e4f8670e","src/aead.rs":"140f77ffb5016836c970c39c6c3a42db9581a14b797b9cd05386d0dd0831fe63","src/aead_fuzzing.rs":"e11ea62b4605b84400edbd5fc394f008fc26d3b67467ba7fe1597b90db4c6a12","src/agent.rs":"1dfab420ca00789f745d440476b272e672ae756536f743d1faa9aa214bc0bcda","src/agentio.rs":"bce4c3dfcfa433209a409ac0c0752f8c95ab37bb6239a42f99b83858e8747bd1","src/auth.rs":"ced1a18f691894984244088020ea25dc1ee678603317f0c7dfc8b8842fa750b4","src/cert.rs":"628fc2384cdeb2a32bfbecb83c99af393df97fd6a76c6e8827e8240c0d47328b","src/constants.rs":"998e77bee88197a240032c1bfbddcff417a25ba82e576a0d2fe18ee9b63cefc7","src/ech.rs":"447f6297f50914249d0c92ec61d74df6c65ce4affceecb8cafe40927e855fe1d","src/err.rs":"f2cc71de2b40d7bba8119eeaee200337db9a0126176ba06e4902d7312facdb58","src/exp.rs":"61586662407359c1ecb8ed4987bc3c702f26ba2e203a091a51b6d6363cbd510f","src/ext.rs":"361277879194dc32f741b8d1894afe5fd3fcc8eb244f7dd5914eeb959b85717d","src/hkdf.rs":"3ff432cc9d40e1dc56e9f983b54b593647c4063a5ae0f16de0a64d033ac9bd94","src/hp.rs":"722a798a7d280b66bd0f8adc6f9135f7d8130f7f0ef45ea12b85d47a88adefda","src/lib.rs":"8d1bfe33999b20eeb5f7eef70d4c634de560de5376029f81b2412bca60176c39","src/once.rs":"b9850384899a1a016e839743d3489c0d4d916e1973746ef8c89872105d7d9736","src/p11.rs":"2ff35a2512bcf894684f24b94640e7fe9d4df6a20264aa3714f2f02fdb99e73d","src/prio.rs":"d9bd43a4d84db70fd552239d0852ea60b960957912f614b929d27dadccca803a","src/replay.rs":"c9bc0261fe1ae22e7212774c315a2669784e57762ca975a15250d4a33dbf3ea3","src/result.rs":"cef34dfcb907723e195b56501132e4560e250b327783cb5e41201da5b63e9b5c","src/secrets.rs":"d5d98e34b100568fecade932832379c52530c70a51ad2d37ee6b2dd008865f01","src/selfencrypt.rs":"f11ae7f6f1e6b602b6e729d71597116fb172190b57102af4f76b22fbe78c8b6a","src/ssl.rs":"ace1a162c3b189d52b5e5f9827c4300b7d47d3b2ae29a976d3bbc46c52328253","src/time.rs":"9bacbed11b0e40402731a9803fd1531272e00e199217c1560130ede50d7020f6","tests/aead.rs":"31b5b4374cc5ca2deee6267c4d5b4858defc74e694ec85af196339a76548a17c","tests/agent.rs":"94819f9eeba2afa0c25adc821755900f1488fd47af6d84d9507a112c29d1752a","tests/ext.rs":"eba9f03accdd598e38292ac88263a81b367d60d5a736a43117a3663de105ec48","tests/handshake.rs":"0fcfa8958686aacb42c56c51c6b234842fe990470d2069a67509869baaa18452","tests/hkdf.rs":"47830c1ea58a02d100522bdde6fabc02bb447ccb85affa0cdc44bc25da1be32a","tests/hp.rs":"316973740210fc1f8166920582795a41347a0aec9024fdc480e2ee83a7a5332d","tests/init.rs":"fc9e392b1efa0d8efb28952f73ffc05e5348e7b2b69207b60e375c3888a252a2","tests/selfencrypt.rs":"1125c858ec4e0a6994f34d162aa066cb003c61b324f268529ea04bcb641347cb"},"package":null}

7
third_party/rust/neqo-crypto/Cargo.toml поставляемый
Просмотреть файл

@ -1,8 +1,9 @@
[package]
name = "neqo-crypto"
version = "0.5.7"
version = "0.6.1"
authors = ["Martin Thomson <mt@lowentropy.net>"]
edition = "2018"
rust-version = "1.57.0"
build = "build.rs"
license = "MIT/Apache-2.0"
@ -11,10 +12,10 @@ neqo-common = { path = "../neqo-common" }
log = {version = "0.4.0", default-features = false}
[build-dependencies]
bindgen = {version = "0.56", default-features = false, features= ["runtime"]}
bindgen = {version = "0.59", default-features = false, features= ["runtime"]}
serde = "1.0"
serde_derive = "1.0"
toml = "0.4"
toml = "0.5"
mozbuild = {version = "0.1", optional = true}
[dev-dependencies]

Просмотреть файл

@ -137,6 +137,7 @@ variables = [
types = [
"CERTCertList",
"CERTCertListNode",
"CK_CHACHA20_PARAMS",
"CK_ATTRIBUTE_TYPE",
"CK_FLAGS",
"CK_MECHANISM_TYPE",
@ -150,6 +151,9 @@ functions = [
"CERT_DestroyCertificate",
"CERT_DestroyCertList",
"CERT_GetCertificateDer",
"PK11_CipherOp",
"PK11_CreateContextBySymKey",
"PK11_DestroyContext",
"PK11_Encrypt",
"PK11_ExtractKeyValue",
"PK11_FindCertFromNickname",
@ -184,6 +188,7 @@ enums = [
]
opaque = [
"CERTCertificate",
"PK11Context",
"PK11SlotInfo",
"PK11SymKey",
"SECKEYPrivateKey",
@ -191,15 +196,16 @@ opaque = [
]
variables = [
"CKA_DERIVE",
"CKA_ENCRYPT",
"CKA_VALUE",
"CKF_DERIVE",
"CKM_AES_ECB",
"CKM_AES_GCM",
"CKM_CHACHA20",
"CKM_CHACHA20_POLY1305",
"CKM_EC_KEY_PAIR_GEN",
"CKM_HKDF_DERIVE",
"CKM_INVALID_MECHANISM",
"CKM_NSS_CHACHA20_CTR",
"CKM_NSS_CHACHA20_POLY1305",
"PK11_ATTR_INSENSITIVE",
"PK11_ATTR_PRIVATE",
"PK11_ATTR_PUBLIC",

15
third_party/rust/neqo-crypto/build.rs поставляемый
Просмотреть файл

@ -91,7 +91,12 @@ fn setup_clang() {
fn nss_dir() -> PathBuf {
let dir = if let Ok(dir) = env::var("NSS_DIR") {
PathBuf::from(dir.trim())
let path = PathBuf::from(dir.trim());
assert!(
!path.is_relative(),
"The NSS_DIR environment variable is expected to be an absolute path."
);
path
} else {
let out_dir = env::var("OUT_DIR").unwrap();
let dir = Path::new(&out_dir).join("nss");
@ -256,16 +261,16 @@ fn build_bindings(base: &str, bindings: &Bindings, flags: &[String], gecko: bool
// Apply the configuration.
for v in &bindings.types {
builder = builder.whitelist_type(v);
builder = builder.allowlist_type(v);
}
for v in &bindings.functions {
builder = builder.whitelist_function(v);
builder = builder.allowlist_function(v);
}
for v in &bindings.variables {
builder = builder.whitelist_var(v);
builder = builder.allowlist_var(v);
}
for v in &bindings.exclude {
builder = builder.blacklist_item(v);
builder = builder.blocklist_item(v);
}
for v in &bindings.opaque {
builder = builder.opaque_type(v);

Просмотреть файл

@ -15,9 +15,6 @@ pub struct Aead {}
#[allow(clippy::unused_self)]
impl Aead {
pub fn new(_version: Version, _cipher: Cipher, _secret: &SymKey, _prefix: &str) -> Res<Self> {
#[cfg(not(debug_assertions))]
panic!("Trying to run a non-debug fuzzing build.");
Ok(Self {})
}

34
third_party/rust/neqo-crypto/src/agent.rs поставляемый
Просмотреть файл

@ -37,7 +37,7 @@ use std::time::Instant;
/// The maximum number of tickets to remember for a given connection.
const MAX_TICKETS: usize = 4;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HandshakeState {
New,
InProgress,
@ -87,8 +87,11 @@ fn get_alpn(fd: *mut ssl::PRFileDesc, pre: bool) -> Res<Option<String>> {
let alpn = match (pre, alpn_state) {
(true, ssl::SSLNextProtoState::SSL_NEXT_PROTO_EARLY_VALUE)
| (false, ssl::SSLNextProtoState::SSL_NEXT_PROTO_NEGOTIATED)
| (false, ssl::SSLNextProtoState::SSL_NEXT_PROTO_SELECTED) => {
| (
false,
ssl::SSLNextProtoState::SSL_NEXT_PROTO_NEGOTIATED
| ssl::SSLNextProtoState::SSL_NEXT_PROTO_SELECTED,
) => {
chosen.truncate(usize::try_from(chosen_len)?);
Some(match String::from_utf8(chosen) {
Ok(a) => a,
@ -191,7 +194,7 @@ impl SecretAgentPreInfo {
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SecretAgentInfo {
version: Version,
cipher: Cipher,
@ -802,6 +805,8 @@ impl ResumptionToken {
pub struct Client {
agent: SecretAgent,
/// The name of the server we're attempting a connection to.
server_name: String,
/// Records the resumption tokens we've received.
resumption: Pin<Box<Vec<ResumptionToken>>>,
}
@ -811,13 +816,15 @@ impl Client {
///
/// # Errors
/// Errors returned if the socket can't be created or configured.
pub fn new(server_name: &str) -> Res<Self> {
pub fn new(server_name: impl Into<String>) -> Res<Self> {
let server_name = server_name.into();
let mut agent = SecretAgent::new()?;
let url = CString::new(server_name)?;
let url = CString::new(server_name.as_bytes())?;
secstatus_to_res(unsafe { ssl::SSL_SetURL(agent.fd, url.as_ptr()) })?;
agent.ready(false)?;
let mut client = Self {
agent,
server_name,
resumption: Box::pin(Vec::new()),
};
client.ready()?;
@ -866,6 +873,11 @@ impl Client {
ssl::SECSuccess
}
#[must_use]
pub fn server_name(&self) -> &str {
&self.server_name
}
fn ready(&mut self) -> Res<()> {
let fd = self.fd;
unsafe {
@ -955,7 +967,7 @@ impl ::std::fmt::Display for Client {
}
/// `ZeroRttCheckResult` encapsulates the options for handling a `ClientHello`.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ZeroRttCheckResult {
/// Accept 0-RTT.
Accept,
@ -1177,8 +1189,8 @@ impl Deref for Agent {
#[must_use]
fn deref(&self) -> &SecretAgent {
match self {
Self::Client(c) => &*c,
Self::Server(s) => &*s,
Self::Client(c) => &**c,
Self::Server(s) => &**s,
}
}
}
@ -1186,8 +1198,8 @@ impl Deref for Agent {
impl DerefMut for Agent {
fn deref_mut(&mut self) -> &mut SecretAgent {
match self {
Self::Client(c) => &mut *c,
Self::Server(s) => &mut *s,
Self::Client(c) => &mut **c,
Self::Server(s) => &mut **s,
}
}
}

2
third_party/rust/neqo-crypto/src/auth.rs поставляемый
Просмотреть файл

@ -7,7 +7,7 @@
use crate::err::{mozpkix, sec, ssl, PRErrorCode};
/// The outcome of authentication.
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AuthenticationStatus {
Ok,
CaInvalid,

8
third_party/rust/neqo-crypto/src/cert.rs поставляемый
Просмотреть файл

@ -5,9 +5,7 @@
// except according to those terms.
use crate::err::secstatus_to_res;
use crate::p11::{
CERTCertListNode, CERT_GetCertificateDer, CertList, Item, PRCList, SECItem, SECItemArray,
};
use crate::p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray};
use crate::ssl::{
PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps,
SSL_PeerStapledOCSPResponses,
@ -15,7 +13,7 @@ use crate::ssl::{
use neqo_common::qerror;
use std::convert::TryFrom;
use std::ptr::NonNull;
use std::ptr::{addr_of, NonNull};
use std::slice;
@ -90,7 +88,7 @@ impl CertificateInfo {
fn head(certs: &CertList) -> *const CERTCertListNode {
// Three stars: one for the reference, one for the wrapper, one to deference the pointer.
unsafe { (&(***certs).list as *const PRCList).cast() }
unsafe { addr_of!((***certs).list).cast() }
}
}

7
third_party/rust/neqo-crypto/src/ech.rs поставляемый
Просмотреть файл

@ -14,7 +14,7 @@ use neqo_common::qtrace;
use std::convert::TryFrom;
use std::ffi::CString;
use std::os::raw::{c_char, c_uint};
use std::ptr::null_mut;
use std::ptr::{addr_of_mut, null_mut};
pub use crate::p11::{HpkeAeadId as AeadId, HpkeKdfId as KdfId, HpkeKemId as KemId};
pub use crate::ssl::HpkeSymmetricSuite as SymmetricSuite;
@ -98,6 +98,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
params.extend_from_slice(oid_slc);
let mut public_ptr: *mut SECKEYPublicKey = null_mut();
let mut param_item = Item::wrap(&params);
// If we have tracing on, try to ensure that key data can be read.
let insensitive_secret_ptr = if log::log_enabled!(log::Level::Trace) {
@ -105,7 +106,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
p11::PK11_GenerateKeyPairWithOpFlags(
*slot,
p11::CK_MECHANISM_TYPE::from(p11::CKM_EC_KEY_PAIR_GEN),
(&mut Item::wrap(&params) as *mut SECItem).cast(),
addr_of_mut!(param_item).cast(),
&mut public_ptr,
p11::PK11_ATTR_SESSION | p11::PK11_ATTR_INSENSITIVE | p11::PK11_ATTR_PUBLIC,
p11::CK_FLAGS::from(p11::CKF_DERIVE),
@ -122,7 +123,7 @@ pub fn generate_keys() -> Res<(PrivateKey, PublicKey)> {
p11::PK11_GenerateKeyPairWithOpFlags(
*slot,
p11::CK_MECHANISM_TYPE::from(p11::CKM_EC_KEY_PAIR_GEN),
(&mut Item::wrap(&params) as *mut SECItem).cast(),
addr_of_mut!(param_item).cast(),
&mut public_ptr,
p11::PK11_ATTR_SESSION | p11::PK11_ATTR_SENSITIVE | p11::PK11_ATTR_PRIVATE,
p11::CK_FLAGS::from(p11::CKF_DERIVE),

3
third_party/rust/neqo-crypto/src/err.rs поставляемый
Просмотреть файл

@ -29,11 +29,10 @@ pub mod nspr {
pub type Res<T> = Result<T, Error>;
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)]
#[allow(renamed_and_removed_lints, clippy::pub_enum_variant_names)] // rust 1.54 will require a different approach
pub enum Error {
AeadInitFailure,
AeadError,
CertificateLoading,
CipherInitFailure,
CreateSslSocket,
EchRetry(Vec<u8>),
HkdfError,

142
third_party/rust/neqo-crypto/src/hp.rs поставляемый
Просмотреть файл

@ -9,14 +9,17 @@ use crate::constants::{
};
use crate::err::{secstatus_to_res, Error, Res};
use crate::p11::{
Item, PK11SymKey, PK11_Encrypt, PK11_GetBlockSize, PK11_GetMechanism, SECItem, SymKey,
CKM_AES_ECB, CKM_NSS_CHACHA20_CTR, CK_MECHANISM_TYPE,
Context, Item, PK11SymKey, PK11_CipherOp, PK11_CreateContextBySymKey, PK11_Encrypt,
PK11_GetBlockSize, SymKey, CKA_ENCRYPT, CKM_AES_ECB, CKM_CHACHA20, CK_ATTRIBUTE_TYPE,
CK_CHACHA20_PARAMS, CK_MECHANISM_TYPE,
};
use std::cell::RefCell;
use std::convert::TryFrom;
use std::fmt::{self, Debug};
use std::os::raw::{c_char, c_uint};
use std::ptr::{null, null_mut};
use std::os::raw::{c_char, c_int, c_uint};
use std::ptr::{addr_of_mut, null, null_mut};
use std::rc::Rc;
experimental_api!(SSL_HkdfExpandLabelWithMech(
version: Version,
@ -32,29 +35,43 @@ experimental_api!(SSL_HkdfExpandLabelWithMech(
));
#[derive(Clone)]
pub struct HpKey(SymKey);
pub enum HpKey {
/// An AES encryption context.
/// Note: as we need to clone this object, we clone the pointer and
/// track references using `Rc`. `PK11Context` can't be used with `PK11_CloneContext`
/// as that is not supported for these contexts.
Aes(Rc<RefCell<Context>>),
/// The ChaCha20 mask has to invoke a new PK11_Encrypt every time as it needs to
/// change the counter and nonce on each invocation.
Chacha(SymKey),
}
impl Debug for HpKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HP-{:?}", self.0)
write!(f, "HpKey")
}
}
impl HpKey {
const SAMPLE_SIZE: usize = 16;
/// QUIC-specific API for extracting a header-protection key.
///
/// # Errors
/// Errors if HKDF fails or if the label is too long to fit in a `c_uint`.
/// # Panics
/// When `cipher` is not known to this code.
#[allow(clippy::cast_sign_loss)] // Cast for PK11_GetBlockSize is safe.
pub fn extract(version: Version, cipher: Cipher, prk: &SymKey, label: &str) -> Res<Self> {
const ZERO: &[u8] = &[0; 12];
let l = label.as_bytes();
let mut secret: *mut PK11SymKey = null_mut();
let (mech, key_size) = match cipher {
TLS_AES_128_GCM_SHA256 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 16),
TLS_AES_256_GCM_SHA384 => (CK_MECHANISM_TYPE::from(CKM_AES_ECB), 32),
TLS_CHACHA20_POLY1305_SHA256 => (CK_MECHANISM_TYPE::from(CKM_NSS_CHACHA20_CTR), 32),
TLS_CHACHA20_POLY1305_SHA256 => (CK_MECHANISM_TYPE::from(CKM_CHACHA20), 32),
_ => unreachable!(),
};
@ -74,18 +91,44 @@ impl HpKey {
&mut secret,
)
}?;
let sym_key = SymKey::from_ptr(secret).or(Err(Error::HkdfError))?;
Ok(HpKey(sym_key))
let key = SymKey::from_ptr(secret).or(Err(Error::HkdfError))?;
let res = match cipher {
TLS_AES_128_GCM_SHA256 | TLS_AES_256_GCM_SHA384 => {
let context_ptr = unsafe {
PK11_CreateContextBySymKey(
mech,
CK_ATTRIBUTE_TYPE::from(CKA_ENCRYPT),
*key,
&Item::wrap(&ZERO[..0]), // Borrow a zero-length slice of ZERO.
)
};
let context = Context::from_ptr(context_ptr).or(Err(Error::CipherInitFailure))?;
Self::Aes(Rc::new(RefCell::new(context)))
}
TLS_CHACHA20_POLY1305_SHA256 => Self::Chacha(key),
_ => unreachable!(),
};
debug_assert_eq!(
res.block_size(),
usize::try_from(unsafe { PK11_GetBlockSize(mech, null_mut()) }).unwrap()
);
Ok(res)
}
/// Get the sample size, which is also the output size.
#[allow(clippy::cast_sign_loss)]
#[must_use]
#[allow(clippy::unused_self)] // To maintain an API contract.
pub fn sample_size(&self) -> usize {
let k: *mut PK11SymKey = *self.0;
let mech = unsafe { PK11_GetMechanism(k) };
// Cast is safe because block size is always greater than or equal to 0
(unsafe { PK11_GetBlockSize(mech, null_mut()) }) as usize
Self::SAMPLE_SIZE
}
fn block_size(&self) -> usize {
match self {
Self::Aes(_) => 16,
Self::Chacha(_) => 64,
}
}
/// Generate a header protection mask for QUIC.
@ -96,36 +139,49 @@ impl HpKey {
/// # Panics
/// When the mechanism for our key is not supported.
pub fn mask(&self, sample: &[u8]) -> Res<Vec<u8>> {
let k: *mut PK11SymKey = *self.0;
let mech = unsafe { PK11_GetMechanism(k) };
let block_size = self.sample_size();
let mut output = vec![0_u8; self.block_size()];
let mut output = vec![0_u8; block_size];
let output_slice = &mut output[..];
let mut output_len: c_uint = 0;
let zero = vec![0_u8; block_size];
let mut wrapped_sample = Item::wrap(sample);
let (iv, inbuf) = match () {
_ if mech == CK_MECHANISM_TYPE::from(CKM_AES_ECB) => (null_mut(), sample),
_ if mech == CK_MECHANISM_TYPE::from(CKM_NSS_CHACHA20_CTR) => {
(&mut wrapped_sample as *mut SECItem, &zero[..])
match self {
Self::Aes(context) => {
let mut output_len: c_int = 0;
secstatus_to_res(unsafe {
PK11_CipherOp(
**context.borrow_mut(),
output.as_mut_ptr(),
&mut output_len,
c_int::try_from(output.len())?,
(&sample[..Self::SAMPLE_SIZE]).as_ptr().cast(),
c_int::try_from(Self::SAMPLE_SIZE).unwrap(),
)
})?;
assert_eq!(usize::try_from(output_len).unwrap(), output.len());
Ok(output)
}
_ => unreachable!(),
};
secstatus_to_res(unsafe {
PK11_Encrypt(
k,
mech,
iv,
output_slice.as_mut_ptr(),
&mut output_len,
c_uint::try_from(output.len())?,
inbuf.as_ptr().cast(),
c_uint::try_from(inbuf.len())?,
)
})?;
assert_eq!(output_len as usize, block_size);
Ok(output)
Self::Chacha(key) => {
let params: CK_CHACHA20_PARAMS = CK_CHACHA20_PARAMS {
pBlockCounter: sample.as_ptr() as *mut u8,
blockCounterBits: 32,
pNonce: (&sample[4..Self::SAMPLE_SIZE]).as_ptr() as *mut _,
ulNonceBits: 96,
};
let mut output_len: c_uint = 0;
let mut param_item = Item::wrap_struct(&params);
secstatus_to_res(unsafe {
PK11_Encrypt(
**key,
CK_MECHANISM_TYPE::from(CKM_CHACHA20),
addr_of_mut!(param_item),
(&mut output[..]).as_mut_ptr(),
&mut output_len,
c_uint::try_from(output.len())?,
(&output[..]).as_ptr(),
c_uint::try_from(output.len())?,
)
})?;
assert_eq!(usize::try_from(output_len).unwrap(), output.len());
Ok(output)
}
}
}
}

3
third_party/rust/neqo-crypto/src/lib.rs поставляемый
Просмотреть файл

@ -73,10 +73,11 @@ use std::ffi::CString;
use std::path::{Path, PathBuf};
use std::ptr::null;
const MINIMUM_NSS_VERSION: &str = "3.66";
const MINIMUM_NSS_VERSION: &str = "3.74";
#[allow(non_upper_case_globals, clippy::redundant_static_lifetimes)]
#[allow(clippy::upper_case_acronyms)]
#[allow(unknown_lints, clippy::borrow_as_ptr)]
mod nss {
include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
}

24
third_party/rust/neqo-crypto/src/p11.rs поставляемый
Просмотреть файл

@ -14,13 +14,15 @@ use crate::err::{secstatus_to_res, Error, Res};
use neqo_common::hex_with_len;
use std::convert::TryFrom;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_uint};
use std::ptr::null_mut;
#[allow(clippy::upper_case_acronyms)]
#[allow(unknown_lints, deref_nullptr)] // Until we require rust 1.53 or bindgen#1651 is fixed.
#[allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
#[allow(clippy::unreadable_literal)]
#[allow(unknown_lints, clippy::borrow_as_ptr)]
mod nss_p11 {
include!(concat!(env!("OUT_DIR"), "/nss_p11.rs"));
}
@ -217,6 +219,11 @@ impl std::fmt::Debug for SymKey {
}
}
unsafe fn destroy_pk11_context(ctxt: *mut PK11Context) {
PK11_DestroyContext(ctxt, PRBool::from(true));
}
scoped_ptr!(Context, PK11Context, destroy_pk11_context);
unsafe fn destroy_secitem(item: *mut SECItem) {
SECITEM_FreeItem(item, PRBool::from(true));
}
@ -225,7 +232,8 @@ scoped_ptr!(Item, SECItem, destroy_secitem);
impl Item {
/// Create a wrapper for a slice of this object.
/// Creating this object is technically safe, but using it is extremely dangerous.
/// Minimally, it can only be passed as a `const SECItem*` argument to functions.
/// Minimally, it can only be passed as a `const SECItem*` argument to functions,
/// or those that treat their argument as `const`.
pub fn wrap(buf: &[u8]) -> SECItem {
SECItem {
type_: SECItemType::siBuffer,
@ -234,6 +242,18 @@ impl Item {
}
}
/// Create a wrapper for a struct.
/// Creating this object is technically safe, but using it is extremely dangerous.
/// Minimally, it can only be passed as a `const SECItem*` argument to functions,
/// or those that treat their argument as `const`.
pub fn wrap_struct<T>(v: &T) -> SECItem {
SECItem {
type_: SECItemType::siBuffer,
data: (v as *const T as *mut T).cast(),
len: c_uint::try_from(mem::size_of::<T>()).unwrap(),
}
}
/// Make an empty `SECItem` for passing as a mutable `SECItem*` argument.
pub fn make_empty() -> SECItem {
SECItem {

6
third_party/rust/neqo-crypto/src/prio.rs поставляемый
Просмотреть файл

@ -5,14 +5,16 @@
// except according to those terms.
#![allow(clippy::upper_case_acronyms)]
#![allow(unknown_lints, deref_nullptr)] // Until we require rust 1.53 or bindgen#1651 is fixed.
#![allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
#![allow(
dead_code,
non_upper_case_globals,
non_snake_case,
clippy::cognitive_complexity,
clippy::empty_enum,
clippy::too_many_lines
clippy::too_many_lines,
unknown_lints,
clippy::borrow_as_ptr
)]
include!(concat!(env!("OUT_DIR"), "/nspr_io.rs"));

2
third_party/rust/neqo-crypto/src/secrets.rs поставляемый
Просмотреть файл

@ -87,7 +87,7 @@ impl Secrets {
}
fn put(&mut self, dir: SecretDirection, epoch: Epoch, key: SymKey) {
qdebug!("{:?} secret available for {:?}", dir, epoch);
qdebug!("{:?} secret available for {:?}: {:?}", dir, epoch, key);
let keys = match dir {
SecretDirection::Read => &mut self.r,
SecretDirection::Write => &mut self.w,

Просмотреть файл

@ -90,7 +90,7 @@ impl SelfEncrypt {
let offset = enc.len();
let mut output: Vec<u8> = enc.into();
output.resize(encoded_len, 0);
cipher.encrypt(0, &extended_aad, plaintext, &mut output[offset..])?;
cipher.encrypt(0, extended_aad.as_ref(), plaintext, &mut output[offset..])?;
qtrace!(
["SelfEncrypt"],
"seal {} {} -> {}",
@ -139,7 +139,8 @@ impl SelfEncrypt {
// NSS insists on having extra space available for decryption.
let padded_len = ciphertext.len() - offset;
let mut output = vec![0; padded_len];
let decrypted = aead.decrypt(0, &extended_aad, &ciphertext[offset..], &mut output)?;
let decrypted =
aead.decrypt(0, extended_aad.as_ref(), &ciphertext[offset..], &mut output)?;
let final_len = decrypted.len();
output.truncate(final_len);
qtrace!(

6
third_party/rust/neqo-crypto/src/ssl.rs поставляемый
Просмотреть файл

@ -10,9 +10,11 @@
non_snake_case,
clippy::cognitive_complexity,
clippy::too_many_lines,
clippy::upper_case_acronyms
clippy::upper_case_acronyms,
unknown_lints,
clippy::borrow_as_ptr
)]
#![allow(unknown_lints, deref_nullptr)] // Until we require rust 1.53 or bindgen#1651 is fixed.
#![allow(unknown_lints, deref_nullptr)] // Until bindgen#1651 is fixed.
use crate::constants::Epoch;
use crate::err::{secstatus_to_res, Res};

10
third_party/rust/neqo-crypto/src/time.rs поставляемый
Просмотреть файл

@ -78,7 +78,7 @@ pub(crate) fn init() {
}
/// Time wraps Instant and provides conversion functions into `PRTime`.
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Time {
t: Instant,
}
@ -119,8 +119,10 @@ impl TryInto<PRTime> for Time {
type Error = Error;
fn try_into(self) -> Res<PRTime> {
let base = get_base();
// TODO(mt) use checked_duration_since when that is available.
let delta = self.t.duration_since(base.instant);
let delta = self
.t
.checked_duration_since(base.instant)
.ok_or(Error::TimeTravelError)?;
if let Ok(d) = PRTime::try_from(delta.as_micros()) {
d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
} else {
@ -137,7 +139,7 @@ impl From<Time> for Instant {
}
/// Interval wraps Duration and provides conversion functions into `PRTime`.
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Interval {
d: Duration,
}

2
third_party/rust/neqo-crypto/tests/aead.rs поставляемый
Просмотреть файл

@ -37,7 +37,7 @@ fn make_aead(cipher: Cipher) -> Aead {
TLS_VERSION_1_3,
cipher,
&secret,
"quic ", // Note the trailing space here.
"quic ", // QUICv1 label prefix; note the trailing space here.
)
.expect("can make an AEAD")
}

51
third_party/rust/neqo-crypto/tests/hp.rs поставляемый
Просмотреть файл

@ -2,18 +2,34 @@
#![warn(clippy::pedantic)]
use neqo_crypto::constants::{
Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_VERSION_1_3,
Cipher, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,
TLS_VERSION_1_3,
};
use neqo_crypto::hkdf;
use neqo_crypto::hp::HpKey;
use std::mem;
use test_fixture::fixture_init;
fn make_hp(cipher: Cipher) -> HpKey {
fixture_init();
let ikm = hkdf::import_key(TLS_VERSION_1_3, &[0; 16]).expect("import IKM");
let prk = hkdf::extract(TLS_VERSION_1_3, cipher, None, &ikm).expect("extract works");
HpKey::extract(TLS_VERSION_1_3, cipher, &prk, "hp").expect("extract label works")
}
fn hp_test(cipher: Cipher, expected: &[u8]) {
let hp = make_hp(cipher);
let mask = hp.mask(&[0; 16]).expect("should produce a mask");
assert_eq!(mask, expected, "first invocation should be correct");
let hp2 = hp.clone();
let mask = hp2.mask(&[0; 16]).expect("clone produces mask");
assert_eq!(mask, expected, "clone should produce the same mask");
let mask = hp.mask(&[0; 16]).expect("should produce a mask again");
assert_eq!(mask, expected, "second invocation should be the same");
}
#[test]
fn aes128() {
const EXPECTED: &[u8] = &[
@ -21,11 +37,7 @@ fn aes128() {
0x14,
];
fixture_init();
let mask = make_hp(TLS_AES_128_GCM_SHA256)
.mask(&[0; 16])
.expect("should produce a mask");
assert_eq!(mask, EXPECTED);
hp_test(TLS_AES_128_GCM_SHA256, EXPECTED);
}
#[test]
@ -35,14 +47,9 @@ fn aes256() {
0x2b,
];
fixture_init();
let mask = make_hp(TLS_AES_256_GCM_SHA384)
.mask(&[0; 16])
.expect("should produce a mask");
assert_eq!(mask, EXPECTED);
hp_test(TLS_AES_256_GCM_SHA384, EXPECTED);
}
#[cfg(feature = "chacha")]
#[test]
fn chacha20_ctr() {
const EXPECTED: &[u8] = &[
@ -53,9 +60,19 @@ fn chacha20_ctr() {
0x2f, 0x52, 0x46, 0x89,
];
fixture_init();
let mask = make_hp(TLS_CHACHA20_POLY1305_SHA256)
.mask(&[0; 16])
.expect("should produce a mask");
assert_eq!(mask, EXPECTED);
hp_test(TLS_CHACHA20_POLY1305_SHA256, EXPECTED);
}
#[test]
#[should_panic]
fn aes_short() {
let hp = make_hp(TLS_AES_128_GCM_SHA256);
mem::drop(hp.mask(&[0; 15]));
}
#[test]
#[should_panic]
fn chacha20_short() {
let hp = make_hp(TLS_CHACHA20_POLY1305_SHA256);
mem::drop(hp.mask(&[0; 15]));
}

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"9ff10a0f34e30e86b9c005058e50ba588b39a78330ece04e29e8b1e3ef060471","src/buffered_send_stream.rs":"0e4ad4914451943e49c88565661d88475ab2bbd8756e200e2a19312bafd64847","src/client_events.rs":"9d86145febad2f3fb05007eae3f5ad4834c78dd709fe388f05590405e34a614b","src/conn_params.rs":"00e3f42636f482f91cd6b86d7bebaa85a9f0407143503767fffb66a3cdfbbe43","src/connection.rs":"ef2e70c08a59a8a60c7852c8beb6545e3cb37d8da94302798bdc2fc38e4f76c0","src/connection_client.rs":"fabd645653c5991b8d2ec6f2a3c4e16e32c4faa292da47f5c5eb405ef39f8a7a","src/connection_server.rs":"de1e0359b902b1e98c923a8d5488302a68a3312b466590fdddaee6ec8327813b","src/control_stream_local.rs":"9ceb1aae8079dfca8e2f38fb555d47e84f3001d9501f2c909e8245841974f49c","src/control_stream_remote.rs":"7a261ac7df77e90a428ab0f92457a934a92a8c581462fc1818efd3de0c0ebd69","src/features/extended_connect/mod.rs":"2bc2f0570b11318f3225173001dad1a5f05e4bf60dee49a2bf9d40e3a411e138","src/features/extended_connect/webtransport_session.rs":"791734e1891bd35541be33cbf744d6edee6278e760fedb12839dd052a0cf91ba","src/features/extended_connect/webtransport_streams.rs":"784c5e317bb6af33f653ba82c1a5666b657c2a210263a415e913494f61613464","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/frames/hframe.rs":"67018ad85ecb9ec0476dafc4b25f6b7015e49531cd19f8e81d204af0e29ee3ea","src/frames/mod.rs":"258dd4bdf2daca19a62cd697d2c7f4709a35668b2b4dce3203675e814c9b40b8","src/frames/reader.rs":"0802cd8b41204bcec424fc6ed704a3bdbed0e5d38444f7a9b0550ad877b076a6","src/frames/tests/hframe.rs":"33a30bb98bb512606a06ae1752e1ed9e4588b7d3f5e9439ec83bb2e779d4ac80","src/frames/tests/mod.rs":"fd2e9d4a28c3bd2fd349f4e3844cefa37e9addb09561e9261b393ca7a37e6c6e","src/frames/tests/reader.rs":"312a3deda7b3a4bbd7afed879c94d0644fce8e34435365ef9cae1fbaa62496af","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"1d87964fe76945bfe3e59834632ce1e3a000b5e26164b71bdcd129f8a4e73ae3","src/headers_checks.rs":"0893d48fde97687b712e86457e75f2a1b802e7589ce38df30ff65684d8cf59c0","src/lib.rs":"4876915dd7f03021cce3a166e12e0a3763ac2c44e6ad81d223cda1f555b7a2c2","src/priority.rs":"ae0fa461031893b4f7e0d12666072e7a4da80b1e8a1c0663ab9f9e27b3242754","src/push_controller.rs":"aa2a64180d8cb1b87682d0d8bbc42167188e8e1890261cb4cabb76de1fcc708b","src/qlog.rs":"44b6cdbb1d9d6ca47b793e9dbe531b8fdbd40147375f7e4c89aeab536c5d286b","src/qpack_decoder_receiver.rs":"75008d8ea5d538ee34aca4df72e58489417604ccafb61b064280782d6754dd0d","src/qpack_encoder_receiver.rs":"f95cc7d49e4d442b93d522f14ddfc581629664d84d6e13d03a520e855bbe442d","src/recv_message.rs":"5f70fb474e387653d7982374131b3b0c08417509469f273ccebf842bfcee836f","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"f3503bf135af5acdb3663a8b591b1db2d160e3dcec37aafd2053f2f150f68d2a","src/server.rs":"3cde23011de0a63ee4900e41368e9319ce100a1584f90bf5463e054adcc8875c","src/server_connection_events.rs":"3d89c2d9a30ee719acfbaae4b7720cb354eb73b11bc6ceb44571d68b05192b8b","src/server_events.rs":"3081fdd1e1950aeecae031452cd683335fb0a9dcec51722e614c5939f747b9d9","src/settings.rs":"8a8919cd31683f476dec281b8b545ea3cedb0c7d60cd1e29b097bae605822d47","src/stream_type_reader.rs":"d63727341d925241ec17c7373d81145aba1464cac4c9eedfc05f24c453435f67","tests/httpconn.rs":"f8d6e6a693d17cf2eb192a730e6fc929bd2814552356ce8d4423a0e3eac8c59d","tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","tests/priority.rs":"a606e5fa03451e09e28c7d5f1820ee85a4567e3969a1690c979761e62123bf54","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport/mod.rs":"635c0b0fe682a844f4366335a40b8b3a6539abe30843ee1bcfaf87a34b1d476c","tests/webtransport/negotiation.rs":"fd46a3a77c75dfb701ac075cdb0aabb58f82b5d5c03c5a965412bbf6ad020f00","tests/webtransport/sessions.rs":"5b4d8483ac018ad5a28adad5e778e2ed48db9c441d1354f6cf21d8e5c6f1a8b3","tests/webtransport/streams.rs":"fd5f075d93f0241290566f59f747d95530d2df579890fd0f6b9e79a557c89a67"},"package":null}
{"files":{"Cargo.toml":"7dd3f73b8b65f903607977de05ae02595340dd5e7afe6ac5010e400fc440c506","src/buffered_send_stream.rs":"4bc45ca03252dc34ab421a2af3499191b182a619143c61d5609a46377c9a0f3d","src/client_events.rs":"9d86145febad2f3fb05007eae3f5ad4834c78dd709fe388f05590405e34a614b","src/conn_params.rs":"7e33526de9c83c163049a2caf2bff0f997351cc61fad76fb6e8c6ec4b9f09938","src/connection.rs":"72c3c2a3c19481d519f4c1928c51dc7c1d4ab6e6cb2bd9ecfdfc5d5c09f485bb","src/connection_client.rs":"e4914a8e44eb6045615382eacf2499b0195574f19ce36e161ad3d3e186f69ebb","src/connection_server.rs":"de1e0359b902b1e98c923a8d5488302a68a3312b466590fdddaee6ec8327813b","src/control_stream_local.rs":"b86e1f869ad59bf2663501942a1a65d94c1dbc3e8770982459e0b620be4b6cf0","src/control_stream_remote.rs":"7a261ac7df77e90a428ab0f92457a934a92a8c581462fc1818efd3de0c0ebd69","src/features/extended_connect/mod.rs":"2bc2f0570b11318f3225173001dad1a5f05e4bf60dee49a2bf9d40e3a411e138","src/features/extended_connect/webtransport_session.rs":"abf84892c429c2ee79efd8e215bfd9da182163ba859cd24b6ee4ba6becceb6bd","src/features/extended_connect/webtransport_streams.rs":"784c5e317bb6af33f653ba82c1a5666b657c2a210263a415e913494f61613464","src/features/mod.rs":"a981ebbd03e7bb7ea2313e883452e44f052c48f28edb7fd53a0825911b490230","src/frames/hframe.rs":"8206e1a27ad805899f7e722c05dffa92649704bbaf98ff2a70a7ca1d6a55395e","src/frames/mod.rs":"258dd4bdf2daca19a62cd697d2c7f4709a35668b2b4dce3203675e814c9b40b8","src/frames/reader.rs":"0802cd8b41204bcec424fc6ed704a3bdbed0e5d38444f7a9b0550ad877b076a6","src/frames/tests/hframe.rs":"33a30bb98bb512606a06ae1752e1ed9e4588b7d3f5e9439ec83bb2e779d4ac80","src/frames/tests/mod.rs":"4933c519069ee4dac23587588f2b792c12d1363e92d0105e1eb169082e213559","src/frames/tests/reader.rs":"312a3deda7b3a4bbd7afed879c94d0644fce8e34435365ef9cae1fbaa62496af","src/frames/tests/wtframe.rs":"589ebe1e62ce4da63b37b7d22cde7ba572ddbf29336fdcdbbcd0a745f79dacd8","src/frames/wtframe.rs":"0eebdf9a275cd53ee6525f7387941601d119d933203d9b2425377adf8348d425","src/headers_checks.rs":"b80c1da2d9f336fa88f7b7f2a834d8e90e826260811771c7729785fdc92b20d4","src/lib.rs":"58d23d794cf5c68d6f2b68e93e3e7d1c5546d64d5d2357ca7cb7858aeb434124","src/priority.rs":"ae0fa461031893b4f7e0d12666072e7a4da80b1e8a1c0663ab9f9e27b3242754","src/push_controller.rs":"aa2a64180d8cb1b87682d0d8bbc42167188e8e1890261cb4cabb76de1fcc708b","src/qlog.rs":"44b6cdbb1d9d6ca47b793e9dbe531b8fdbd40147375f7e4c89aeab536c5d286b","src/qpack_decoder_receiver.rs":"75008d8ea5d538ee34aca4df72e58489417604ccafb61b064280782d6754dd0d","src/qpack_encoder_receiver.rs":"f95cc7d49e4d442b93d522f14ddfc581629664d84d6e13d03a520e855bbe442d","src/recv_message.rs":"5f70fb474e387653d7982374131b3b0c08417509469f273ccebf842bfcee836f","src/request_target.rs":"9182b641f7a7b55272e0e2e872d24a35b1207f35399221b08b899857c3e873ab","src/send_message.rs":"9e1b22ede2a105a79d7c02178801e1a46b06a80dc1c0d2a7d69b0eea7e89f319","src/server.rs":"ab00f395f7767d733091af3e3317527e5c302b2e5062b33943211ede75f10109","src/server_connection_events.rs":"3d89c2d9a30ee719acfbaae4b7720cb354eb73b11bc6ceb44571d68b05192b8b","src/server_events.rs":"3081fdd1e1950aeecae031452cd683335fb0a9dcec51722e614c5939f747b9d9","src/settings.rs":"e7babcce34c49d897c7d5ed93ef8e9ad02524cebff96a249c2ce84f1b968be21","src/stream_type_reader.rs":"f790b2aaa6758ad85487d98376895b5ee2c3098ffd4586825e1bb0b3c2375c75","tests/httpconn.rs":"f8d6e6a693d17cf2eb192a730e6fc929bd2814552356ce8d4423a0e3eac8c59d","tests/mod.rs":"fd6aee37243713e80fc526552f21f0222338cec9890409b6575a2a637b17ec1f","tests/priority.rs":"a606e5fa03451e09e28c7d5f1820ee85a4567e3969a1690c979761e62123bf54","tests/send_message.rs":"673ae1d0bf2dce46c21ee8353f45f189d2cb64a2f6e137ae38da6b2262ad066e","tests/webtransport/mod.rs":"635c0b0fe682a844f4366335a40b8b3a6539abe30843ee1bcfaf87a34b1d476c","tests/webtransport/negotiation.rs":"2da85dfd45e3dfdbab7608768d734e1f150e1b0ba14e982cbb6de16ba62789c2","tests/webtransport/sessions.rs":"5b4d8483ac018ad5a28adad5e778e2ed48db9c441d1354f6cf21d8e5c6f1a8b3","tests/webtransport/streams.rs":"fd5f075d93f0241290566f59f747d95530d2df579890fd0f6b9e79a557c89a67"},"package":null}

3
third_party/rust/neqo-http3/Cargo.toml поставляемый
Просмотреть файл

@ -1,8 +1,9 @@
[package]
name = "neqo-http3"
version = "0.5.7"
version = "0.6.1"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"
license = "MIT/Apache-2.0"
[dependencies]

Просмотреть файл

@ -8,7 +8,7 @@ use crate::Res;
use neqo_common::qtrace;
use neqo_transport::{Connection, StreamId};
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum BufferedStream {
Uninitialized,
Initialized { stream_id: StreamId, buf: Vec<u8> },

Просмотреть файл

@ -14,7 +14,7 @@ const QPACK_MAX_BLOCKED_STREAMS_DEFAULT: u16 = 20;
const MAX_PUSH_STREAM_DEFAULT: u64 = 0;
const WEBTRANSPORT_DEFAULT: bool = false;
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct Http3Parameters {
conn_params: ConnectionParameters,
qpack_settings: QpackSettings,

12
third_party/rust/neqo-http3/src/connection.rs поставляемый
Просмотреть файл

@ -203,8 +203,8 @@ impl Http3Connection {
self.qpack_decoder.borrow_mut().send(conn)?;
match self.qpack_encoder.borrow_mut().send_encoder_updates(conn) {
Ok(())
| Err(neqo_qpack::Error::EncoderStreamBlocked)
| Err(neqo_qpack::Error::DynamicTableFull) => {}
| Err(neqo_qpack::Error::EncoderStreamBlocked | neqo_qpack::Error::DynamicTableFull) => {
}
Err(e) => return Err(Error::QpackError(e)),
}
Ok(())
@ -300,9 +300,9 @@ impl Http3Connection {
}
Ok(ReceiveOutput::ControlFrames(rest))
}
ReceiveOutput::NewStream(NewStreamType::Push(_))
| ReceiveOutput::NewStream(NewStreamType::Http)
| ReceiveOutput::NewStream(NewStreamType::WebTransportStream(_)) => Ok(output),
ReceiveOutput::NewStream(
NewStreamType::Push(_) | NewStreamType::Http | NewStreamType::WebTransportStream(_),
) => Ok(output),
ReceiveOutput::NewStream(_) => {
unreachable!("NewStream should have been handled already")
}
@ -355,7 +355,7 @@ impl Http3Connection {
match state {
State::Handshaking => {
if self.role == Role::Server
&& conn.zero_rtt_state() == &ZeroRttState::AcceptedServer
&& conn.zero_rtt_state() == ZeroRttState::AcceptedServer
{
self.state = Http3State::ZeroRtt;
self.initialize_http3_connection(conn)?;

Просмотреть файл

@ -22,7 +22,7 @@ use neqo_crypto::{agent::CertificateInfo, AuthenticationStatus, ResumptionToken,
use neqo_qpack::Stats as QpackStats;
use neqo_transport::{
AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdGenerator, Output,
QuicVersion, Stats as TransportStats, StreamId, StreamType, ZeroRttState,
Stats as TransportStats, StreamId, StreamType, Version, ZeroRttState,
};
use std::cell::RefCell;
use std::fmt::Debug;
@ -49,13 +49,13 @@ where
}
}
fn alpn_from_quic_version(version: QuicVersion) -> &'static str {
fn alpn_from_quic_version(version: Version) -> &'static str {
match version {
QuicVersion::Version1 => "h3",
QuicVersion::Draft29 => "h3-29",
QuicVersion::Draft30 => "h3-30",
QuicVersion::Draft31 => "h3-31",
QuicVersion::Draft32 => "h3-32",
Version::Version2 | Version::Version1 => "h3",
Version::Draft29 => "h3-29",
Version::Draft30 => "h3-30",
Version::Draft31 => "h3-31",
Version::Draft32 => "h3-32",
}
}
@ -77,7 +77,7 @@ impl Http3Client {
/// Making a `neqo-transport::connection` may produce an error. This can only be a crypto error if
/// the socket can't be created or configured.
pub fn new(
server_name: &str,
server_name: impl Into<String>,
cid_manager: Rc<RefCell<dyn ConnectionIdGenerator>>,
local_addr: SocketAddr,
remote_addr: SocketAddr,
@ -90,12 +90,13 @@ impl Http3Client {
&[alpn_from_quic_version(
http3_parameters
.get_connection_parameters()
.get_quic_version(),
.get_versions()
.initial(),
)],
cid_manager,
local_addr,
remote_addr,
*http3_parameters.get_connection_parameters(),
http3_parameters.get_connection_parameters().clone(),
now,
)?,
http3_parameters,
@ -105,17 +106,16 @@ impl Http3Client {
#[must_use]
pub fn new_with_conn(c: Connection, http3_parameters: Http3Parameters) -> Self {
let events = Http3ClientEvents::default();
let webtransport = http3_parameters.get_webtransport();
let push_streams = http3_parameters.get_max_concurrent_push_streams();
let mut base_handler = Http3Connection::new(http3_parameters, Role::Client);
if http3_parameters.get_webtransport() {
if webtransport {
base_handler.set_features_listener(events.clone());
}
Self {
conn: c,
events: events.clone(),
push_handler: Rc::new(RefCell::new(PushController::new(
http3_parameters.get_max_concurrent_push_streams(),
events,
))),
push_handler: Rc::new(RefCell::new(PushController::new(push_streams, events))),
base_handler,
}
}
@ -219,7 +219,7 @@ impl Http3Client {
debug_assert_eq!(Ok(true), res);
return Err(Error::FatalError);
}
if *self.conn.zero_rtt_state() == ZeroRttState::Sending {
if self.conn.zero_rtt_state() == ZeroRttState::Sending {
self.base_handler
.set_0rtt_settings(&mut self.conn, settings)?;
self.events
@ -834,17 +834,16 @@ mod tests {
use neqo_common::{event::Provider, qtrace, Datagram, Decoder, Encoder};
use neqo_crypto::{AllowZeroRtt, AntiReplay, ResumptionToken};
use neqo_qpack::{encoder::QPackEncoder, QpackSettings};
use neqo_transport::tparams::{self, TransportParameter};
use neqo_transport::{
ConnectionError, ConnectionEvent, ConnectionParameters, Output, State, StreamId,
StreamType, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE,
StreamType, Version, RECV_BUFFER_SIZE, SEND_BUFFER_SIZE,
};
use std::convert::TryFrom;
use std::mem;
use std::time::Duration;
use test_fixture::{
addr, anti_replay, default_server_h3, fixture_init, now, CountingConnectionIdGenerator,
DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
addr, anti_replay, default_server_h3, fixture_init, new_server, now,
CountingConnectionIdGenerator, DEFAULT_ALPN_H3, DEFAULT_KEYS, DEFAULT_SERVER_NAME,
};
fn assert_closed(client: &Http3Client, expected: &Error) {
@ -869,6 +868,11 @@ mod tests {
addr(),
addr(),
Http3Parameters::default()
.connection_parameters(
// Disable compatible upgrade, which complicates tests.
ConnectionParameters::default()
.versions(Version::default(), vec![Version::default()]),
)
.max_table_size_encoder(max_table_size)
.max_table_size_decoder(max_table_size)
.max_blocked_streams(100)
@ -1017,9 +1021,9 @@ mod tests {
self.settings.encode(&mut enc);
assert_eq!(
self.conn
.stream_send(self.control_stream_id.unwrap(), &enc[..])
.stream_send(self.control_stream_id.unwrap(), enc.as_ref())
.unwrap(),
enc[..].len()
enc.len()
);
}
@ -1160,19 +1164,10 @@ mod tests {
.borrow_mut()
.encode_header_block(&mut self.conn, headers, stream_id);
let hframe = HFrame::Headers {
header_block: header_block.to_vec(),
header_block: header_block.as_ref().to_vec(),
};
hframe.encode(encoder);
}
pub fn set_max_uni_stream(&mut self, max_stream: u64) {
self.conn
.set_local_tparam(
tparams::INITIAL_MAX_STREAMS_UNI,
TransportParameter::Integer(max_stream),
)
.unwrap();
}
}
fn handshake_only(client: &mut Http3Client, server: &mut TestServer) -> Output {
@ -1424,10 +1419,13 @@ mod tests {
client: &mut Http3Client,
server: &mut TestServer,
stream_id: StreamId,
response: &[u8],
response: impl AsRef<[u8]>,
close_stream: bool,
) {
let _ = server.conn.stream_send(stream_id, response).unwrap();
let _ = server
.conn
.stream_send(stream_id, response.as_ref())
.unwrap();
if close_stream {
server.conn.stream_close_send(stream_id).unwrap();
}
@ -1459,7 +1457,7 @@ mod tests {
};
let mut d = Encoder::default();
frame.encode(&mut d);
let _ = conn.stream_send(stream_id, &d).unwrap();
let _ = conn.stream_send(stream_id, d.as_ref()).unwrap();
}
fn send_push_data_and_exchange_packets(
@ -1500,7 +1498,7 @@ mod tests {
frame.encode(&mut d);
server
.conn
.stream_send(server.control_stream_id.unwrap(), &d)
.stream_send(server.control_stream_id.unwrap(), d.as_ref())
.unwrap();
let out = server.conn.process(None, now());
@ -1536,13 +1534,13 @@ mod tests {
conn: &mut Connection,
push_stream_id: StreamId,
push_id: u8,
data: &[u8],
data: impl AsRef<[u8]>,
close_push_stream: bool,
) {
// send data
let _ = conn.stream_send(push_stream_id, PUSH_STREAM_TYPE).unwrap();
let _ = conn.stream_send(push_stream_id, &[push_id]).unwrap();
let _ = conn.stream_send(push_stream_id, data).unwrap();
let _ = conn.stream_send(push_stream_id, data.as_ref()).unwrap();
if close_push_stream {
conn.stream_close_send(push_stream_id).unwrap();
}
@ -2412,7 +2410,7 @@ mod tests {
let mut enc = Encoder::default();
data_frame.encode(&mut enc);
(vec![0_u8; size], enc.to_vec())
(vec![0_u8; size], enc.as_ref().to_vec())
}
// Send 2 frames. For the second one we can only send 63 bytes.
@ -3892,7 +3890,7 @@ mod tests {
server.settings.encode(&mut enc);
let mut sent = server.conn.stream_send(control_stream, CONTROL_STREAM_TYPE);
assert_eq!(sent.unwrap(), CONTROL_STREAM_TYPE.len());
sent = server.conn.stream_send(control_stream, &enc);
sent = server.conn.stream_send(control_stream, enc.as_ref());
assert_eq!(sent.unwrap(), enc.len());
let out = server.conn.process(None, now());
@ -4531,7 +4529,10 @@ mod tests {
let d_frame = HFrame::Data { len: 3 };
d_frame.encode(&mut d);
d.encode(&[0x61, 0x62, 0x63]);
let _ = server.conn.stream_send(request_stream_id, &d[..]).unwrap();
let _ = server
.conn
.stream_send(request_stream_id, d.as_ref())
.unwrap();
server.conn.stream_close_send(request_stream_id).unwrap();
let out = server.conn.process(None, now());
@ -6087,9 +6088,9 @@ mod tests {
for f in H3_RESERVED_FRAME_TYPES {
let mut enc = Encoder::default();
enc.encode_varint(*f);
test_wrong_frame_on_control_stream(&enc);
test_wrong_frame_on_push_stream(&enc);
test_wrong_frame_on_request_stream(&enc);
test_wrong_frame_on_control_stream(enc.as_ref());
test_wrong_frame_on_push_stream(enc.as_ref());
test_wrong_frame_on_request_stream(enc.as_ref());
}
}
@ -6110,7 +6111,7 @@ mod tests {
// The settings frame contains a reserved settings type and some value (0x1).
enc.encode_varint(*s);
enc.encode_varint(1_u64);
let sent = server.conn.stream_send(control_stream, &enc);
let sent = server.conn.stream_send(control_stream, enc.as_ref());
assert_eq!(sent, Ok(4));
let out = server.conn.process(None, now());
client.process(out.dgram(), now());
@ -6369,8 +6370,10 @@ mod tests {
#[test]
fn client_control_stream_create_failed() {
let mut client = default_http3_client();
let mut server = TestServer::new();
server.set_max_uni_stream(0);
let mut server = TestServer::new_with_conn(new_server(
DEFAULT_ALPN_H3,
ConnectionParameters::default().max_streams(StreamType::UniDi, 0),
));
handshake_client_error(&mut client, &mut server, &Error::StreamLimitError);
}
@ -6378,8 +6381,10 @@ mod tests {
#[test]
fn client_qpack_stream_create_failed() {
let mut client = default_http3_client();
let mut server = TestServer::new();
server.set_max_uni_stream(2);
let mut server = TestServer::new_with_conn(new_server(
DEFAULT_ALPN_H3,
ConnectionParameters::default().max_streams(StreamType::UniDi, 2),
));
handshake_client_error(&mut client, &mut server, &Error::StreamLimitError);
}

Просмотреть файл

@ -39,7 +39,7 @@ impl ControlStreamLocal {
pub fn queue_frame(&mut self, f: &HFrame) {
let mut enc = Encoder::default();
f.encode(&mut enc);
self.stream.buffer(&enc);
self.stream.buffer(enc.as_ref());
}
pub fn queue_update_priority(&mut self, stream_id: StreamId) {
@ -80,7 +80,7 @@ impl ControlStreamLocal {
if let Some(hframe) = stream.priority_update_frame() {
let mut enc = Encoder::new();
hframe.encode(&mut enc);
if self.stream.send_atomic(conn, &enc)? {
if self.stream.send_atomic(conn, enc.as_ref())? {
stream.priority_update_sent();
} else {
self.outstanding_priority_update.push_front(update_id);

Просмотреть файл

@ -377,7 +377,8 @@ impl WebTransportSession {
};
let mut encoder = Encoder::default();
close_frame.encode(&mut encoder);
self.control_stream_send.send_data_atomic(conn, &encoder)?;
self.control_stream_send
.send_data_atomic(conn, encoder.as_ref())?;
self.control_stream_send.close(conn)?;
self.state = if self.control_stream_send.done() {
SessionState::Done

Просмотреть файл

@ -26,7 +26,7 @@ pub const H3_FRAME_TYPE_PRIORITY_UPDATE_PUSH: HFrameType = 0xf0701;
pub const H3_RESERVED_FRAME_TYPES: &[HFrameType] = &[0x2, 0x6, 0x8, 0x9];
// data for DATA frame is not read into HFrame::Data.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum HFrame {
Data {
len: u64, // length of the data
@ -138,7 +138,7 @@ impl HFrame {
update_frame.encode(&priority_enc);
enc.encode_varint(update_frame.len() as u64);
enc.encode(&update_frame);
enc.encode(update_frame.as_ref());
}
}
}

Просмотреть файл

@ -17,7 +17,7 @@ use test_fixture::{default_client, default_server, now};
pub fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T {
// For data, headers and push_promise we do not read all bytes from the buffer
let d2 = Encoder::from_hex(st);
assert_eq!(&d[..], &d2[..d.len()]);
assert_eq!(d.as_ref(), &d2.as_ref()[..d.as_ref().len()]);
let mut conn_c = default_client();
let mut conn_s = default_server();
@ -36,7 +36,7 @@ pub fn enc_dec<T: FrameDecoder<T>>(d: &Encoder, st: &str, remaining: usize) -> T
// conver string into u8 vector
let buf = Encoder::from_hex(st);
conn_s.stream_send(stream_id, &buf[..]).unwrap();
conn_s.stream_send(stream_id, buf.as_ref()).unwrap();
let out = conn_s.process(None, now());
mem::drop(conn_c.process(out.dgram(), now()));

Просмотреть файл

@ -13,7 +13,7 @@ pub(crate) type WebTransportFrameType = u64;
const WT_FRAME_CLOSE_SESSION: WebTransportFrameType = 0x2843;
const WT_FRAME_CLOSE_MAX_MESSAGE_SIZE: u64 = 1024;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum WebTransportFrame {
CloseSession { error: u32, message: String },
}

Просмотреть файл

@ -114,7 +114,7 @@ pub fn headers_valid(headers: &[Header], message_type: MessageType) -> Res<()> {
let pseudo_header_mask = match message_type {
MessageType::Response => enum_set!(PseudoHeaderState::Status),
MessageType::Request => {
if method_value == Some(&"CONNECT".to_string()) {
if method_value == Some("CONNECT") {
PseudoHeaderState::Method | PseudoHeaderState::Authority
} else {
PseudoHeaderState::Method | PseudoHeaderState::Scheme | PseudoHeaderState::Path
@ -124,7 +124,7 @@ pub fn headers_valid(headers: &[Header], message_type: MessageType) -> Res<()> {
if (MessageType::Request == message_type)
&& pseudo_state.contains(PseudoHeaderState::Protocol)
&& method_value != Some(&"CONNECT".to_string())
&& method_value != Some("CONNECT")
{
return Err(Error::InvalidHeader);
}

19
third_party/rust/neqo-http3/src/lib.rs поставляемый
Просмотреть файл

@ -59,12 +59,7 @@ pub use stream_type_reader::NewStreamType;
type Res<T> = Result<T, Error>;
#[derive(Clone, Debug, PartialEq)]
#[allow(
renamed_and_removed_lints,
clippy::pub_enum_variant_names,
clippy::enum_variant_names
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
HttpNoError,
HttpGeneralProtocol,
@ -153,8 +148,7 @@ impl Error {
| Self::HttpId
| Self::HttpSettings
| Self::HttpMissingSettings
| Self::QpackError(QpackError::EncoderStream)
| Self::QpackError(QpackError::DecoderStream)
| Self::QpackError(QpackError::EncoderStream | QpackError::DecoderStream)
)
}
@ -168,10 +162,9 @@ impl Error {
#[must_use]
pub fn map_stream_send_errors(err: &Error) -> Self {
match err {
Self::TransportError(TransportError::InvalidStreamId)
| Self::TransportError(TransportError::FinalSizeError) => {
Error::TransportStreamDoesNotExist
}
Self::TransportError(
TransportError::InvalidStreamId | TransportError::FinalSizeError,
) => Error::TransportStreamDoesNotExist,
Self::TransportError(TransportError::InvalidInput) => Error::InvalidInput,
_ => {
debug_assert!(false, "Unexpected error");
@ -305,7 +298,7 @@ pub enum Http3StreamType {
}
#[must_use]
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum ReceiveOutput {
NoOutput,
PushStream,

Просмотреть файл

@ -210,7 +210,7 @@ impl SendStream for SendMessage {
data_frame.encode(&mut enc);
let sent_fh = self
.stream
.send_atomic(conn, &enc)
.send_atomic(conn, enc.as_ref())
.map_err(|e| Error::map_stream_send_errors(&e))?;
debug_assert!(sent_fh);
@ -300,7 +300,7 @@ impl SendStream for SendMessage {
};
let mut enc = Encoder::default();
data_frame.encode(&mut enc);
self.stream.buffer(&enc);
self.stream.buffer(enc.as_ref());
self.stream.buffer(buf);
mem::drop(self.stream.send_buffer(conn)?);
Ok(())

35
third_party/rust/neqo-http3/src/server.rs поставляемый
Просмотреть файл

@ -62,9 +62,9 @@ impl Http3Server {
protocols,
anti_replay,
zero_rtt_checker
.unwrap_or_else(|| Box::new(HttpZeroRttChecker::new(http3_parameters))),
.unwrap_or_else(|| Box::new(HttpZeroRttChecker::new(http3_parameters.clone()))),
cid_manager,
*http3_parameters.get_connection_parameters(),
http3_parameters.get_connection_parameters().clone(),
)?,
http3_parameters,
http3_handlers: HashMap::new(),
@ -150,10 +150,12 @@ impl Http3Server {
fn process_events(&mut self, conn: &mut ActiveConnectionRef, now: Instant) {
let mut remove = false;
let http3_parameters = self.http3_parameters;
let http3_parameters = &self.http3_parameters;
{
let handler = self.http3_handlers.entry(conn.clone()).or_insert_with(|| {
Rc::new(RefCell::new(Http3ServerHandler::new(http3_parameters)))
Rc::new(RefCell::new(Http3ServerHandler::new(
http3_parameters.clone(),
)))
});
handler
.borrow_mut()
@ -342,12 +344,7 @@ mod tests {
fn assert_closed(hconn: &mut Http3Server, expected: &Error) {
let err = ConnectionError::Application(expected.code());
let closed = |e| {
matches!(e,
Http3ServerEvent::StateChange{ state: Http3State::Closing(e), .. }
| Http3ServerEvent::StateChange{ state: Http3State::Closed(e), .. }
if e == err)
};
let closed = |e| matches!(e, Http3ServerEvent::StateChange{ state: Http3State::Closing(e) | Http3State::Closed(e), .. } if e == err);
assert!(hconn.events().any(closed));
}
@ -614,7 +611,7 @@ mod tests {
};
let mut e = Encoder::default();
frame.encode(&mut e);
peer_conn.control_send(&e);
peer_conn.control_send(e.as_ref());
let out = peer_conn.process(None, now());
hconn.process(out.dgram(), now());
// check if the given connection got closed on invalid stream ids
@ -1143,7 +1140,7 @@ mod tests {
/// Perform a handshake, then another with the token from the first.
/// The second should always resume, but it might not always accept early data.
fn zero_rtt_with_settings(conn_params: Http3Parameters, zero_rtt: &ZeroRttState) {
fn zero_rtt_with_settings(conn_params: Http3Parameters, zero_rtt: ZeroRttState) {
let (_, mut client) = connect();
let token = client.events().find_map(|e| {
if let ConnectionEvent::ResumptionToken(token) = e {
@ -1165,7 +1162,7 @@ mod tests {
#[test]
fn zero_rtt() {
zero_rtt_with_settings(http3params(DEFAULT_SETTINGS), &ZeroRttState::AcceptedClient);
zero_rtt_with_settings(http3params(DEFAULT_SETTINGS), ZeroRttState::AcceptedClient);
}
/// A larger QPACK decoder table size isn't an impediment to 0-RTT.
@ -1176,7 +1173,7 @@ mod tests {
max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder + 1,
..DEFAULT_SETTINGS
}),
&ZeroRttState::AcceptedClient,
ZeroRttState::AcceptedClient,
);
}
@ -1188,7 +1185,7 @@ mod tests {
max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder - 1,
..DEFAULT_SETTINGS
}),
&ZeroRttState::Rejected,
ZeroRttState::Rejected,
);
}
@ -1200,7 +1197,7 @@ mod tests {
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams + 1,
..DEFAULT_SETTINGS
}),
&ZeroRttState::AcceptedClient,
ZeroRttState::AcceptedClient,
);
}
@ -1212,7 +1209,7 @@ mod tests {
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams - 1,
..DEFAULT_SETTINGS
}),
&ZeroRttState::Rejected,
ZeroRttState::Rejected,
);
}
@ -1224,7 +1221,7 @@ mod tests {
max_table_size_encoder: DEFAULT_SETTINGS.max_table_size_encoder - 1,
..DEFAULT_SETTINGS
}),
&ZeroRttState::AcceptedClient,
ZeroRttState::AcceptedClient,
);
}
@ -1304,6 +1301,6 @@ mod tests {
connect_transport(&mut server, &mut client, true);
assert!(client.tls_info().unwrap().resumed());
assert_eq!(client.zero_rtt_state(), &ZeroRttState::Rejected);
assert_eq!(client.zero_rtt_state(), ZeroRttState::Rejected);
}
}

6
third_party/rust/neqo-http3/src/settings.rs поставляемый
Просмотреть файл

@ -24,7 +24,7 @@ const SETTINGS_ENABLE_WEB_TRANSPORT: SettingsType = 0x2b60_3742;
pub const H3_RESERVED_SETTINGS: &[SettingsType] = &[0x2, 0x3, 0x4, 0x5];
#[derive(Clone, PartialEq, Debug, Copy)]
#[derive(Clone, PartialEq, Eq, Debug, Copy)]
pub enum HSettingType {
MaxHeaderListSize,
MaxTableCapacity,
@ -41,7 +41,7 @@ fn hsetting_default(setting_type: HSettingType) -> u64 {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HSetting {
pub setting_type: HSettingType,
pub value: u64,
@ -57,7 +57,7 @@ impl HSetting {
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct HSettings {
settings: Vec<HSetting>,
}

Просмотреть файл

@ -18,7 +18,7 @@ pub const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1;
pub const WEBTRANSPORT_UNI_STREAM: u64 = 0x54;
pub const WEBTRANSPORT_STREAM: u64 = 0x41;
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NewStreamType {
Control,
Decoder,
@ -59,7 +59,6 @@ impl NewStreamType {
}
(_, StreamType::BiDi, Role::Server) => Err(Error::HttpFrame),
(HTTP3_UNI_STREAM_TYPE_PUSH, StreamType::UniDi, Role::Server)
| (H3_FRAME_TYPE_HEADERS, StreamType::BiDi, Role::Client)
| (_, StreamType::BiDi, Role::Client) => Err(Error::HttpStreamCreation),
_ => Ok(Some(NewStreamType::Unknown)),
}
@ -197,13 +196,13 @@ impl NewStreamHeadReader {
fn map_stream_fin(decoded: Option<NewStreamType>) -> Res<Option<NewStreamType>> {
match decoded {
Some(NewStreamType::Control)
| Some(NewStreamType::Encoder)
| Some(NewStreamType::Decoder) => Err(Error::HttpClosedCriticalStream),
Some(NewStreamType::Control | NewStreamType::Encoder | NewStreamType::Decoder) => {
Err(Error::HttpClosedCriticalStream)
}
None => Err(Error::HttpStreamCreation),
Some(NewStreamType::Http) => Err(Error::HttpFrame),
Some(NewStreamType::Unknown) => Ok(decoded),
Some(NewStreamType::Push(_)) | Some(NewStreamType::WebTransportStream(_)) => {
Some(NewStreamType::Push(_) | NewStreamType::WebTransportStream(_)) => {
unreachable!("PushStream and WebTransport are mapped to None at this stage.")
}
}
@ -318,7 +317,7 @@ mod tests {
for i in to_encode {
enc.encode_varint(*i);
}
self.decode_buffer(&enc[..], fin, outcome, done);
self.decode_buffer(enc.as_ref(), fin, outcome, done);
}
}

Просмотреть файл

@ -139,8 +139,8 @@ fn wrong_setting_value() {
};
settings.encode(&mut enc);
assert_eq!(
server.stream_send(control, &enc[..]).unwrap(),
enc[..].len()
server.stream_send(control, enc.as_ref()).unwrap(),
enc.as_ref().len()
);
exchange_packets2(&mut client, &mut server);

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"ffbf61e358a81f58d8b4f0ca89031d53879ec8e45851a0f1bc2b583229feb815","src/decoder.rs":"8bd336c91cca989883106a9d0bf26b117d224e0e7643960c3e97d0168d1853c4","src/decoder_instructions.rs":"19d47158bc09551b449be205f5cd5ea83e6984c4e4d3e7d4b95938b09617015e","src/encoder.rs":"e72cbcdbe24cbe13ad5cbcbb0df8981a2ea67331f296ec7784480bc28ae01eef","src/encoder_instructions.rs":"a7f1d3a4f8ae941286d0aba81037a8df3ef85e275392ef31d9938e9314c706db","src/header_block.rs":"7910ddc28b44d2065070cb2d87ab3cfbb905cce912b23d8b12b0f0add5691ceb","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"29c5e47f8a4cf9c0a5dfdc614594868db22bc25b9688e5efdbc041cd222a17e5","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"49ded6607ec0859cb3edc5a38ff48f4d2d292f0721673d4e20700d07ac324557","src/reader.rs":"be265cc8c317512f266fafdcc835d0e413caf5280a7cc945bfe6e7e849529d67","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}
{"files":{"Cargo.toml":"9c7bc0f5de79c42f0957ab114f8a4ca38c276cc9f90e3823dc3b5e3a85226b66","src/decoder.rs":"8bd336c91cca989883106a9d0bf26b117d224e0e7643960c3e97d0168d1853c4","src/decoder_instructions.rs":"2205c7635b8f0c568f6fe9a63c17028eaf8d29a9b5ac7136b6554cc7fbf35038","src/encoder.rs":"b888a819595fec47037d508b943f5ff04ed52ea376bef1f90e08edc6576e773c","src/encoder_instructions.rs":"1eb4f6eee2d9ff16f96dc5bf80dae9bc04316126f6eca933fb51dbd9218a439c","src/header_block.rs":"76f4c8fad6a13d4d24530cf067d20622cdbd345f7d9779b0be9691a77fa8fb63","src/huffman.rs":"3a9edaf827343ec6e43cfd50fcc0d0077287947160ae630da5c3ddaaefedd010","src/huffman_decode_helper.rs":"2970c57f052878b727c2f764490c54184f5c2608e1d6aa961c3b01509e290122","src/huffman_table.rs":"06fea766a6276ac56c7ee0326faed800a742c15fda1f33bf2513e6cc6a5e6d27","src/lib.rs":"6e25612bb30f0e4361566662da1e5353131ae12f97938c6ac3b2dafbf6a8bc86","src/prefix.rs":"72c587c40aef4ed38cf13b2de91091d671611679be2a9da6f0b24abafaf50dc5","src/qlog.rs":"7618085e27bb3fb1f4d1c73ba501b9a293723293c4020b7cc4129676eb278131","src/qpack_send_buf.rs":"49ded6607ec0859cb3edc5a38ff48f4d2d292f0721673d4e20700d07ac324557","src/reader.rs":"be265cc8c317512f266fafdcc835d0e413caf5280a7cc945bfe6e7e849529d67","src/static_table.rs":"fda9d5c6f38f94b0bf92d3afdf8432dce6e27e189736596e16727090c77b78ec","src/stats.rs":"624dfa3b40858c304097bb0ce5b1be1bb4d7916b1abfc222f1aa705907009730","src/table.rs":"f7091bdd9ad1f8fe3b2298a7dbfd3d285c212d69569cda54f9bcf251cb758a21"},"package":null}

3
third_party/rust/neqo-qpack/Cargo.toml поставляемый
Просмотреть файл

@ -1,8 +1,9 @@
[package]
name = "neqo-qpack"
version = "0.5.7"
version = "0.6.1"
authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
edition = "2018"
rust-version = "1.57.0"
license = "MIT/Apache-2.0"
[dependencies]

Просмотреть файл

@ -14,7 +14,7 @@ use neqo_common::{qdebug, qtrace};
use neqo_transport::StreamId;
use std::mem;
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DecoderInstruction {
InsertCountIncrement { increment: u64 },
HeaderAck { stream_id: StreamId },

5
third_party/rust/neqo-qpack/src/encoder.rs поставляемый
Просмотреть файл

@ -516,7 +516,7 @@ mod tests {
use crate::QpackSettings;
use neqo_transport::{ConnectionParameters, StreamId, StreamType};
use std::mem;
use test_fixture::{configure_server, default_client, default_server, handshake, now};
use test_fixture::{default_client, default_server, handshake, new_server, now, DEFAULT_ALPN};
struct TestEncoder {
encoder: QPackEncoder,
@ -571,7 +571,8 @@ mod tests {
fn connect_generic(huffman: bool, max_data: Option<u64>) -> TestEncoder {
let mut conn = default_client();
let mut peer_conn = max_data.map_or_else(default_server, |max| {
configure_server(
new_server(
DEFAULT_ALPN,
ConnectionParameters::default()
.max_stream_data(StreamType::UniDi, true, max)
.max_stream_data(StreamType::BiDi, true, max)

Просмотреть файл

@ -18,7 +18,7 @@ use std::mem;
// We may decide to use othe instruction in the future.
// All instructions are used for testing, therefore they are defined.
#[allow(dead_code)]
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum EncoderInstruction<'a> {
Capacity { value: u64 },
InsertWithNameRefStatic { index: u64, value: &'a [u8] },
@ -63,7 +63,7 @@ enum EncoderInstructionReaderState {
Done,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum DecodedEncoderInstruction {
Capacity { value: u64 },
InsertWithNameRefStatic { index: u64, value: Vec<u8> },

Просмотреть файл

@ -177,7 +177,7 @@ impl<'a> ::std::fmt::Display for HeaderDecoder<'a> {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum HeaderDecoderResult {
Blocked(u64),
Headers(Vec<Header>),

7
third_party/rust/neqo-qpack/src/lib.rs поставляемый
Просмотреть файл

@ -38,12 +38,7 @@ pub struct QpackSettings {
pub max_blocked_streams: u16,
}
#[derive(Clone, Debug, PartialEq)]
#[allow(
renamed_and_removed_lints,
clippy::pub_enum_variant_names,
clippy::enum_variant_names
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
DecompressionFailed,
EncoderStream,

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

3
third_party/rust/neqo-transport/Cargo.toml поставляемый
Просмотреть файл

@ -1,8 +1,9 @@
[package]
name = "neqo-transport"
version = "0.5.7"
version = "0.6.1"
authors = ["EKR <ekr@rtfm.com>", "Andy Grover <agrover@mozilla.com>"]
edition = "2018"
rust-version = "1.57.0"
license = "MIT/Apache-2.0"
[dependencies]

Просмотреть файл

@ -127,7 +127,7 @@ impl AddressValidation {
// Include the token identifier ("Retry"/~) in the AAD, then keep it for plaintext.
let mut buf = Self::encode_aad(peer_address, retry);
let encrypted = self.self_encrypt.seal(&buf, &data)?;
let encrypted = self.self_encrypt.seal(buf.as_ref(), data.as_ref())?;
buf.truncate(TOKEN_IDENTIFIER_RETRY.len());
buf.encode(&encrypted);
Ok(buf.into())
@ -165,7 +165,7 @@ impl AddressValidation {
now: Instant,
) -> Option<ConnectionId> {
let peer_addr = Self::encode_aad(peer_address, retry);
let data = self.self_encrypt.open(&peer_addr, token).ok()?;
let data = self.self_encrypt.open(peer_addr.as_ref(), token).ok()?;
let mut dec = Decoder::new(&data);
match dec.decode_uint(4) {
Some(d) => {

Просмотреть файл

@ -152,7 +152,7 @@ impl<T: WindowAdjustment> CongestionControl for ClassicCongestionControl<T> {
let is_app_limited = self.app_limited();
qtrace!(
[self],
"app limited={}, bytes_in_flight:{}, cwnd: {}, state: {:?} pacing_burst_size: {}",
"limited={}, bytes_in_flight={}, cwnd={}, state={:?} pacing_burst_size={}",
is_app_limited,
self.bytes_in_flight,
self.congestion_window,
@ -413,7 +413,7 @@ impl<T: WindowAdjustment> ClassicCongestionControl<T> {
continue;
}
if let Some(t) = start {
if p.time_sent.duration_since(t) > pc_period {
if p.time_sent.checked_duration_since(t).unwrap() > pc_period {
qinfo!([self], "persistent congestion");
self.congestion_window = CWND_MIN;
self.acked_bytes = 0;

6
third_party/rust/neqo-transport/src/cid.rs поставляемый
Просмотреть файл

@ -450,6 +450,10 @@ impl ConnectionIdManager {
}
}
pub fn generator(&self) -> Rc<RefCell<dyn ConnectionIdGenerator>> {
Rc::clone(&self.generator)
}
pub fn decoder(&self) -> ConnectionIdDecoderRef {
ConnectionIdDecoderRef {
generator: self.generator.deref().borrow(),
@ -488,6 +492,8 @@ impl ConnectionIdManager {
/// During the handshake, a server needs to regard the client's choice of destination
/// connection ID as valid. This function saves it in the store in a special place.
/// Note that this is only done *after* an Initial packet from the client is
/// successfully processed.
pub fn add_odcid(&mut self, cid: ConnectionId) {
let entry = ConnectionIdEntry::new(CONNECTION_ID_SEQNO_ODCID, cid, ());
self.connection_ids.add_local(entry);

Просмотреть файл

@ -41,9 +41,7 @@ use crate::frame::{
CloseError, Frame, FrameType, FRAME_TYPE_CONNECTION_CLOSE_APPLICATION,
FRAME_TYPE_CONNECTION_CLOSE_TRANSPORT,
};
use crate::packet::{
DecryptedPacket, PacketBuilder, PacketNumber, PacketType, PublicPacket, QuicVersion,
};
use crate::packet::{DecryptedPacket, PacketBuilder, PacketNumber, PacketType, PublicPacket};
use crate::path::{Path, PathRef, Paths};
use crate::quic_datagrams::{DatagramTracking, QuicDatagrams};
use crate::recovery::{LossRecovery, RecoveryToken, SendProfile};
@ -52,10 +50,12 @@ pub use crate::send_stream::{RetransmissionPriority, TransmissionPriority};
use crate::stats::{Stats, StatsCell};
use crate::stream_id::StreamType;
use crate::streams::Streams;
use crate::tparams::{self, TransportParameter, TransportParameters, TransportParametersHandler};
use crate::tparams::{
self, TransportParameter, TransportParameterId, TransportParameters, TransportParametersHandler,
};
use crate::tracking::{AckTracker, PacketNumberSpace, SentPacket};
use crate::{qlog, StreamId};
use crate::{AppError, ConnectionError, Error, Res};
use crate::version::{Version, WireVersion};
use crate::{qlog, AppError, ConnectionError, Error, Res, StreamId};
mod idle;
pub mod params;
@ -79,7 +79,7 @@ struct Packet(Vec<u8>);
/// handshake. This is a hack, but a useful one.
const EXTRA_INITIALS: usize = 4;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ZeroRttState {
Init,
Sending,
@ -88,7 +88,7 @@ pub enum ZeroRttState {
Rejected,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
/// Type returned from process() and `process_output()`. Users are required to
/// call these repeatedly until `Callback` or `None` is returned.
pub enum Output {
@ -218,6 +218,7 @@ impl AddressValidationInfo {
/// remote) continue processing until `state()` returns `Closed`.
pub struct Connection {
role: Role,
version: Version,
state: State,
tps: Rc<RefCell<TransportParametersHandler>>,
/// What we are doing with 0-RTT.
@ -246,6 +247,8 @@ pub struct Connection {
/// when they are either just reordered or we haven't been able to install keys yet.
/// In particular, this occurs when asynchronous certificate validation happens.
saved_datagrams: SavedDatagrams,
/// Some packets were received, but not tracked.
received_untracked: bool,
/// This is responsible for the QuicDatagrams' handling:
/// https://datatracker.ietf.org/doc/html/draft-ietf-quic-datagram
@ -293,7 +296,7 @@ impl Connection {
/// Create a new QUIC connection with Client role.
pub fn new_client(
server_name: &str,
server_name: impl Into<String>,
protocols: &[impl AsRef<str>],
cid_generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
local_addr: SocketAddr,
@ -304,12 +307,16 @@ impl Connection {
let dcid = ConnectionId::generate_initial();
let mut c = Self::new(
Role::Client,
Client::new(server_name)?.into(),
Agent::from(Client::new(server_name.into())?),
cid_generator,
protocols,
conn_params,
)?;
c.crypto.states.init(c.version(), Role::Client, &dcid);
c.crypto.states.init(
c.conn_params.get_versions().compatible(),
Role::Client,
&dcid,
);
c.original_destination_cid = Some(dcid);
let path = Path::temporary(
local_addr,
@ -331,18 +338,18 @@ impl Connection {
) -> Res<Self> {
Self::new(
Role::Server,
Server::new(certs)?.into(),
Agent::from(Server::new(certs)?),
cid_generator,
protocols,
conn_params,
)
}
fn new(
fn new<P: AsRef<str>>(
role: Role,
agent: Agent,
cid_generator: Rc<RefCell<dyn ConnectionIdGenerator>>,
protocols: &[impl AsRef<str>],
protocols: &[P],
conn_params: ConnectionParameters,
) -> Res<Self> {
// Setup the local connection ID.
@ -360,9 +367,9 @@ impl Connection {
let tphandler = Rc::new(RefCell::new(tps));
let crypto = Crypto::new(
conn_params.get_quic_version(),
conn_params.get_versions().initial(),
agent,
protocols,
protocols.iter().map(P::as_ref).map(String::from).collect(),
Rc::clone(&tphandler),
)?;
@ -377,6 +384,7 @@ impl Connection {
let c = Self {
role,
version: conn_params.get_versions().initial(),
state: State::Init,
paths: Paths::default(),
cid_manager,
@ -387,6 +395,7 @@ impl Connection {
remote_initial_source_cid: None,
original_destination_cid: None,
saved_datagrams: SavedDatagrams::default(),
received_untracked: false,
crypto,
acks: AckTracker::default(),
idle_timeout: IdleTimeout::new(conn_params.get_idle_timeout()),
@ -457,13 +466,15 @@ impl Connection {
}
/// Set a local transport parameter, possibly overriding a default value.
/// In general, this method should not be used. This only sets transport parameters
/// without dealing with other aspects of setting the value.
pub fn set_local_tparam(
&self,
tp: crate::tparams::TransportParameterId,
value: TransportParameter,
) -> Res<()> {
/// This only sets transport parameters without dealing with other aspects of
/// setting the value.
/// # Panics
/// This panics if the transport parameter is known to this crate.
pub fn set_local_tparam(&self, tp: TransportParameterId, value: TransportParameter) -> Res<()> {
#[cfg(not(test))]
{
assert!(!tparams::INTERNAL_TRANSPORT_PARAMETERS.contains(&tp));
}
if *self.state() == State::Init {
self.tps.borrow_mut().local.set(tp, value);
Ok(())
@ -538,6 +549,7 @@ impl Connection {
.remote
.as_ref()
.expect("should have transport parameters"),
self.version,
u64::try_from(rtt.as_millis()).unwrap_or(0),
)
.unwrap()
@ -638,6 +650,13 @@ impl Connection {
);
let mut dec = Decoder::from(token.as_ref());
let version =
Version::try_from(dec.decode_uint(4).ok_or(Error::InvalidResumptionToken)? as u32)?;
qtrace!([self], " version {:?}", version);
if !self.conn_params.get_versions().all().contains(&version) {
return Err(Error::DisabledVersion);
}
let rtt = Duration::from_millis(dec.decode_varint().ok_or(Error::InvalidResumptionToken)?);
qtrace!([self], " RTT {:?}", rtt);
@ -652,6 +671,7 @@ impl Connection {
let tok = dec.decode_remainder();
qtrace!([self], " TLS token {}", hex(&tok));
match self.crypto.tls {
Agent::Client(ref mut c) => {
let res = c.enable_resumption(&tok);
@ -663,6 +683,9 @@ impl Connection {
Agent::Server(_) => return Err(Error::WrongRole),
}
self.version = version;
self.conn_params.get_versions_mut().set_initial(version);
self.tps.borrow_mut().set_version(version);
self.tps.borrow_mut().remote_0rtt = Some(tp);
if !init_token.is_empty() {
self.address_validation = AddressValidationInfo::NewToken(init_token.to_vec());
@ -695,7 +718,7 @@ impl Connection {
tps.borrow().local.encode(enc_inner);
});
enc.encode(extra);
let records = s.send_ticket(now, &enc)?;
let records = s.send_ticket(now, enc.as_ref())?;
qinfo!([self], "send session ticket {}", hex(&enc));
self.crypto.buffer_records(records)?;
} else {
@ -740,7 +763,7 @@ impl Connection {
pub fn authenticated(&mut self, status: AuthenticationStatus, now: Instant) {
qinfo!([self], "Authenticated {:?}", status);
self.crypto.tls.authenticated(status);
let res = self.handshake(now, PacketNumberSpace::Handshake, None);
let res = self.handshake(now, self.version, PacketNumberSpace::Handshake, None);
self.absorb_error(now, res);
self.process_saved(now);
}
@ -756,13 +779,13 @@ impl Connection {
}
/// The QUIC version in use.
pub fn version(&self) -> QuicVersion {
self.conn_params.get_quic_version()
pub fn version(&self) -> Version {
self.version
}
/// Get the 0-RTT state of the connection.
pub fn zero_rtt_state(&self) -> &ZeroRttState {
&self.zero_rtt_state
pub fn zero_rtt_state(&self) -> ZeroRttState {
self.zero_rtt_state
}
/// Get a snapshot of collected statistics.
@ -1000,7 +1023,7 @@ impl Connection {
self.process_output(now)
}
fn handle_retry(&mut self, packet: &PublicPacket) {
fn handle_retry(&mut self, packet: &PublicPacket, now: Instant) {
qinfo!([self], "received Retry");
if matches!(self.address_validation, AddressValidationInfo::Retry { .. }) {
self.stats.borrow_mut().pkt_dropped("Extra Retry");
@ -1029,12 +1052,14 @@ impl Connection {
retry_scid
);
let lost_packets = self.loss_recovery.retry(&path);
let lost_packets = self.loss_recovery.retry(&path, now);
self.handle_lost_packets(&lost_packets);
self.crypto
.states
.init(self.version(), self.role, &retry_scid);
self.crypto.states.init(
self.conn_params.get_versions().compatible(),
self.role,
&retry_scid,
);
self.address_validation = AddressValidationInfo::Retry {
token: packet.token().to_vec(),
retry_source_cid: retry_scid,
@ -1086,7 +1111,7 @@ impl Connection {
fn process_saved(&mut self, now: Instant) {
while let Some(cspace) = self.saved_datagrams.available() {
qdebug!([self], "process saved for space {:?}", cspace);
debug_assert!(self.crypto.states.rx_hp(cspace).is_some());
debug_assert!(self.crypto.states.rx_hp(self.version, cspace).is_some());
for saved in self.saved_datagrams.take_saved() {
qtrace!([self], "input saved @{:?}: {:?}", saved.t, saved.d);
self.input(saved.d, saved.t, now);
@ -1106,6 +1131,44 @@ impl Connection {
self.stats.borrow_mut().saved_datagrams += 1;
}
/// Perform version negotiation.
fn version_negotiation(&mut self, supported: &[WireVersion], now: Instant) -> Res<()> {
debug_assert_eq!(self.role, Role::Client);
if let Some(version) = self.conn_params.get_versions().preferred(supported) {
assert_ne!(self.version, version);
qinfo!([self], "Version negotiation: trying {:?}", version);
let local_addr = self.paths.primary().borrow().local_address();
let remote_addr = self.paths.primary().borrow().remote_address();
let conn_params = self
.conn_params
.clone()
.versions(version, self.conn_params.get_versions().all().to_vec());
let mut c = Self::new_client(
self.crypto.server_name().unwrap(),
self.crypto.protocols(),
self.cid_manager.generator(),
local_addr,
remote_addr,
conn_params,
now,
)?;
c.conn_params
.get_versions_mut()
.set_initial(self.conn_params.get_versions().initial());
mem::swap(self, &mut c);
Ok(())
} else {
qinfo!([self], "Version negotiation: failed with {:?}", supported);
// This error goes straight to closed.
self.set_state(State::Closed(ConnectionError::Transport(
Error::VersionNegotiation,
)));
Err(Error::VersionNegotiation)
}
}
/// Perform any processing that we might have to do on packets prior to
/// attempting to remove protection.
fn preprocess_packet(
@ -1135,7 +1198,10 @@ impl Connection {
match (packet.packet_type(), &self.state, &self.role) {
(PacketType::Initial, State::Init, Role::Server) => {
if !packet.is_valid_initial() {
let version = *packet.version().as_ref().unwrap();
if !packet.is_valid_initial()
|| !self.conn_params.get_versions().all().contains(&version)
{
self.stats.borrow_mut().pkt_dropped("Invalid Initial");
return Ok(PreprocessResult::Next);
}
@ -1145,10 +1211,12 @@ impl Connection {
packet.scid(),
packet.dcid()
);
// Record the client's selected CID so that it can be accepted until
// the client starts using a real connection ID.
let dcid = ConnectionId::from(packet.dcid());
self.crypto.states.init_server(version, &dcid);
self.original_destination_cid = Some(dcid);
self.set_state(State::WaitInitial);
self.crypto
.states
.init(self.version(), self.role, packet.dcid());
// We need to make sure that we set this transport parameter.
// This has to happen prior to processing the packet so that
@ -1164,8 +1232,9 @@ impl Connection {
match packet.supported_versions() {
Ok(versions) => {
if versions.is_empty()
|| versions.contains(&self.version().as_u32())
|| packet.dcid() != self.odcid().unwrap()
|| versions.contains(&self.version().wire_version())
|| versions.contains(&0)
|| packet.scid() != self.odcid().unwrap()
|| matches!(
self.address_validation,
AddressValidationInfo::Retry { .. }
@ -1178,19 +1247,17 @@ impl Connection {
return Ok(PreprocessResult::End);
}
self.set_state(State::Closed(ConnectionError::Transport(
Error::VersionNegotiation,
)));
return Err(Error::VersionNegotiation);
self.version_negotiation(&versions, now)?;
return Ok(PreprocessResult::End);
}
Err(_) => {
self.stats.borrow_mut().pkt_dropped("Invalid VN");
self.stats.borrow_mut().pkt_dropped("VN with no versions");
return Ok(PreprocessResult::End);
}
}
}
(PacketType::Retry, State::WaitInitial, Role::Client) => {
self.handle_retry(packet);
self.handle_retry(packet, now);
return Ok(PreprocessResult::Next);
}
(PacketType::Handshake, State::WaitInitial, Role::Client)
@ -1226,7 +1293,7 @@ impl Connection {
PreprocessResult::Next
}
State::WaitInitial => PreprocessResult::Continue,
State::Handshaking | State::Connected | State::Confirmed => {
State::WaitVersion | State::Handshaking | State::Connected | State::Confirmed => {
if !self.cid_manager.is_valid(packet.dcid()) {
self.stats
.borrow_mut()
@ -1269,6 +1336,7 @@ impl Connection {
if self.state == State::WaitInitial {
self.start_handshake(path, packet, now);
}
if self.state.connected() {
self.handle_migration(path, d, migrate, now);
} else if self.role != Role::Client
@ -1365,6 +1433,12 @@ impl Connection {
// Exhausting read keys is fatal.
return Err(e);
}
Error::KeysDiscarded(cspace) => {
// This was a valid-appearing Initial packet: maybe probe with
// a Handshake packet to keep the handshake moving.
self.received_untracked |=
self.role == Role::Client && cspace == CryptoSpace::Initial;
}
_ => (),
}
// Decryption failure, or not having keys is not fatal.
@ -1418,15 +1492,30 @@ impl Connection {
ack_eliciting |= f.ack_eliciting();
probing &= f.path_probing();
let t = f.get_type();
if let Err(e) = self.input_frame(path, packet.packet_type(), f, now) {
if let Err(e) = self.input_frame(path, packet.version(), packet.packet_type(), f, now) {
self.capture_error(Some(Rc::clone(path)), now, t, Err(e))?;
}
}
let largest_received = self
let largest_received = if let Some(space) = self
.acks
.get_mut(PacketNumberSpace::from(packet.packet_type()))
.unwrap()
.set_received(now, packet.pn(), ack_eliciting);
{
space.set_received(now, packet.pn(), ack_eliciting)
} else {
qdebug!(
[self],
"processed a {:?} packet without tracking it",
packet.packet_type(),
);
// This was a valid packet that caused the same packet number to be
// discarded. This happens when the client discards the Initial packet
// number space after receiving the ServerHello. Remember this so
// that we guarantee that we send a Handshake packet.
self.received_untracked = true;
// We don't migrate during the handshake, so return false.
false
};
Ok(largest_received && !probing)
}
@ -1434,6 +1523,7 @@ impl Connection {
/// During connection setup, the first path needs to be setup.
/// This uses the connection IDs that were provided during the handshake
/// to setup that path.
#[allow(clippy::or_fun_call)] // Remove when MSRV >= 1.59
fn setup_handshake_path(&mut self, path: &PathRef, now: Instant) {
self.paths.make_permanent(
path,
@ -1443,7 +1533,7 @@ impl Connection {
ConnectionIdEntry::initial_remote(
self.remote_initial_source_cid
.as_ref()
.or_else(|| self.original_destination_cid.as_ref())
.or(self.original_destination_cid.as_ref())
.unwrap()
.clone(),
),
@ -1495,29 +1585,35 @@ impl Connection {
debug_assert_eq!(packet.packet_type(), PacketType::Initial);
self.remote_initial_source_cid = Some(ConnectionId::from(packet.scid()));
if self.role == Role::Server {
// Record the client's selected CID so that it can be accepted until
// the client starts using a real connection ID.
let dcid = ConnectionId::from(packet.dcid());
self.original_destination_cid = Some(dcid.clone());
self.cid_manager.add_odcid(dcid);
let got_version = if self.role == Role::Server {
self.cid_manager
.add_odcid(self.original_destination_cid.as_ref().unwrap().clone());
// Make a path on which to run the handshake.
self.setup_handshake_path(path, now);
self.zero_rtt_state = match self.crypto.enable_0rtt(self.role) {
self.zero_rtt_state = match self.crypto.enable_0rtt(self.version, self.role) {
Ok(true) => {
qdebug!([self], "Accepted 0-RTT");
ZeroRttState::AcceptedServer
}
_ => ZeroRttState::Rejected,
};
// The server knows the final version if it has remote transport parameters.
self.tps.borrow().remote.is_some()
} else {
qdebug!([self], "Changing to use Server CID={}", packet.scid());
debug_assert!(path.borrow().is_primary());
path.borrow_mut().set_remote_cid(packet.scid());
}
self.set_state(State::Handshaking);
// The client knows the final version if it processed a CRYPTO frame.
self.stats.borrow().frame_rx.crypto > 0
};
if got_version {
self.set_state(State::Handshaking);
} else {
self.set_state(State::WaitVersion);
}
}
/// Migrate to the provided path.
@ -1647,6 +1743,7 @@ impl Connection {
let res = match &self.state {
State::Init
| State::WaitInitial
| State::WaitVersion
| State::Handshaking
| State::Connected
| State::Confirmed => {
@ -1676,7 +1773,7 @@ impl Connection {
encoder: Encoder,
tx: &CryptoDxState,
address_validation: &AddressValidationInfo,
quic_version: QuicVersion,
version: Version,
grease_quic_bit: bool,
) -> (PacketType, PacketBuilder) {
let pt = PacketType::from(cspace);
@ -1691,13 +1788,7 @@ impl Connection {
path.local_cid(),
);
PacketBuilder::long(
encoder,
pt,
quic_version,
path.remote_cid(),
path.local_cid(),
)
PacketBuilder::long(encoder, pt, version, path.remote_cid(), path.local_cid())
};
if builder.remaining() > 0 {
builder.scramble(grease_quic_bit);
@ -1748,11 +1839,12 @@ impl Connection {
let grease_quic_bit = self.can_grease_quic_bit();
let version = self.version();
for space in PacketNumberSpace::iter() {
let (cspace, tx) = if let Some(crypto) = self.crypto.states.select_tx_mut(*space) {
crypto
} else {
continue;
};
let (cspace, tx) =
if let Some(crypto) = self.crypto.states.select_tx_mut(self.version, *space) {
crypto
} else {
continue;
};
let path = close.path().borrow();
let (_, mut builder) = Self::build_packet_header(
@ -1885,13 +1977,18 @@ impl Connection {
tokens: &mut Vec<RecoveryToken>,
now: Instant,
) -> bool {
let untracked = self.received_untracked && !self.state.connected();
self.received_untracked = false;
// Anything written after an ACK already elicits acknowledgment.
// If we need to probe and nothing has been written, send a PING.
if builder.len() > ack_end {
return true;
}
let probe = if force_probe {
// The packet might be empty, but we need to probe.
let probe = if untracked && builder.packet_empty() || force_probe {
// If we received an untracked packet and we aren't probing already
// or the PTO timer fired: probe.
true
} else {
let pto = path.borrow().rtt().pto(PacketNumberSpace::ApplicationData);
@ -2012,11 +2109,12 @@ impl Connection {
let mut encoder = Encoder::with_capacity(profile.limit());
for space in PacketNumberSpace::iter() {
// Ensure we have tx crypto state for this epoch, or skip it.
let (cspace, tx) = if let Some(crypto) = self.crypto.states.select_tx_mut(*space) {
crypto
} else {
continue;
};
let (cspace, tx) =
if let Some(crypto) = self.crypto.states.select_tx_mut(self.version, *space) {
crypto
} else {
continue;
};
let header_start = encoder.len();
let (pt, mut builder) = Self::build_packet_header(
@ -2059,17 +2157,25 @@ impl Connection {
continue;
}
dump_packet(self, path, "TX ->", pt, pn, &builder[payload_start..]);
dump_packet(
self,
path,
"TX ->",
pt,
pn,
&builder.as_ref()[payload_start..],
);
qlog::packet_sent(
&mut self.qlog,
pt,
pn,
builder.len() - header_start + aead_expansion,
&builder[payload_start..],
&builder.as_ref()[payload_start..],
);
self.stats.borrow_mut().packets_tx += 1;
encoder = builder.build(self.crypto.states.tx_mut(cspace).unwrap())?;
let tx = self.crypto.states.tx_mut(self.version, cspace).unwrap();
encoder = builder.build(tx)?;
debug_assert!(encoder.len() <= mtu);
self.crypto.states.auto_update()?;
@ -2099,14 +2205,13 @@ impl Connection {
self.loss_recovery.on_packet_sent(path, sent);
}
if *space == PacketNumberSpace::Handshake {
if self.role == Role::Client {
// Client can send Handshake packets -> discard Initial keys and states
self.discard_keys(PacketNumberSpace::Initial, now);
} else if self.state == State::Confirmed {
// We could discard handshake keys in set_state, but wait until after sending an ACK.
self.discard_keys(PacketNumberSpace::Handshake, now);
}
if *space == PacketNumberSpace::Handshake
&& self.role == Role::Server
&& self.state == State::Confirmed
{
// We could discard handshake keys in set_state,
// but wait until after sending an ACK.
self.discard_keys(PacketNumberSpace::Handshake, now);
}
}
@ -2150,9 +2255,9 @@ impl Connection {
debug_assert_eq!(self.role, Role::Client);
qlog::client_connection_started(&mut self.qlog, &self.paths.primary());
self.handshake(now, PacketNumberSpace::Initial, None)?;
self.handshake(now, self.version, PacketNumberSpace::Initial, None)?;
self.set_state(State::WaitInitial);
self.zero_rtt_state = if self.crypto.enable_0rtt(self.role)? {
self.zero_rtt_state = if self.crypto.enable_0rtt(self.version, self.role)? {
qdebug!([self], "Enabled 0-RTT");
ZeroRttState::Sending
} else {
@ -2205,6 +2310,7 @@ impl Connection {
/// Process the final set of transport parameters.
fn process_tps(&mut self) -> Res<()> {
self.validate_cids()?;
self.validate_versions()?;
{
let tps = self.tps.borrow();
let remote = tps.remote.as_ref().unwrap();
@ -2315,9 +2421,91 @@ impl Connection {
Ok(())
}
/// Validate the `version_negotiation` transport parameter from the peer.
fn validate_versions(&mut self) -> Res<()> {
let tph = self.tps.borrow();
let remote_tps = tph.remote.as_ref().unwrap();
// `current` and `other` are the value from the peer's transport parameters.
// We're checking that these match our expectations.
if let Some((current, other)) = remote_tps.get_versions() {
qtrace!(
[self],
"validate_versions: current={:x} chosen={:x} other={:x?}",
self.version.wire_version(),
current,
other,
);
if self.role == Role::Server {
// 1. A server acts on transport parameters, with validation
// of `current` happening in the transport parameter handler.
// All we need to do is confirm that the transport parameter
// was provided.
Ok(())
} else if self.version().wire_version() != current {
qinfo!([self], "validate_versions: current version mismatch");
Err(Error::VersionNegotiation)
} else if self
.conn_params
.get_versions()
.initial()
.is_compatible(self.version)
{
// 2. The current version is compatible with what we attempted.
// That's a compatible upgrade and that's OK.
Ok(())
} else {
// 3. The initial version we attempted isn't compatible. Check that
// the one we would have chosen is compatible with this one.
let mut all_versions = other.to_owned();
all_versions.push(current);
if self
.conn_params
.get_versions()
.preferred(&all_versions)
.ok_or(Error::VersionNegotiation)?
.is_compatible(self.version)
{
Ok(())
} else {
qinfo!([self], "validate_versions: failed");
Err(Error::VersionNegotiation)
}
}
} else if self.version != Version::Version1 && !self.version.is_draft() {
qinfo!([self], "validate_versions: missing extension");
Err(Error::VersionNegotiation)
} else {
Ok(())
}
}
fn confirm_version(&mut self, v: Version) {
if self.version != v {
qinfo!([self], "Compatible upgrade {:?} ==> {:?}", self.version, v);
}
self.crypto.confirm_version(v);
self.version = v;
}
fn compatible_upgrade(&mut self, packet_version: Version) {
if !matches!(self.state, State::WaitInitial | State::WaitVersion) {
return;
}
if self.role == Role::Client {
self.confirm_version(packet_version);
} else if self.tps.borrow().remote.is_some() {
let version = self.tps.borrow().version();
let dcid = self.original_destination_cid.as_ref().unwrap();
self.crypto.states.init_server(version, dcid);
self.confirm_version(version);
}
}
fn handshake(
&mut self,
now: Instant,
packet_version: Version,
space: PacketNumberSpace,
data: Option<&[u8]>,
) -> Res<()> {
@ -2341,13 +2529,19 @@ impl Connection {
}
// There is a chance that this could be called less often, but getting the
// conditions right is a little tricky, so call it on every CRYPTO frame.
// conditions right is a little tricky, so call whenever CRYPTO data is used.
if try_update {
self.compatible_upgrade(packet_version);
// We have transport parameters, it's go time.
if self.tps.borrow().remote.is_some() {
self.set_initial_limits();
}
if self.crypto.install_keys(self.role)? {
if self.role == Role::Client {
// We won't acknowledge Initial packets as a result of this, but the
// server can rely on implicit acknowledgment.
self.discard_keys(PacketNumberSpace::Initial, now);
}
self.saved_datagrams.make_available(CryptoSpace::Handshake);
}
}
@ -2358,16 +2552,17 @@ impl Connection {
fn input_frame(
&mut self,
path: &PathRef,
ptype: PacketType,
packet_version: Version,
packet_type: PacketType,
frame: Frame,
now: Instant,
) -> Res<()> {
if !frame.is_allowed(ptype) {
qinfo!("frame not allowed: {:?} {:?}", frame, ptype);
if !frame.is_allowed(packet_type) {
qinfo!("frame not allowed: {:?} {:?}", frame, packet_type);
return Err(Error::ProtocolViolation);
}
self.stats.borrow_mut().frame_rx.all += 1;
let space = PacketNumberSpace::from(ptype);
let space = PacketNumberSpace::from(packet_type);
if frame.is_stream() {
return self
.streams
@ -2412,7 +2607,7 @@ impl Connection {
let mut buf = Vec::new();
let read = self.crypto.streams.read_to_end(space, &mut buf);
qdebug!("Read {} bytes", read);
self.handshake(now, space, Some(&buf))?;
self.handshake(now, packet_version, space, Some(&buf))?;
self.create_resumption_token(now);
} else {
// If we get a useless CRYPTO frame send outstanding CRYPTO frames again.
@ -2616,14 +2811,14 @@ impl Connection {
}
/// When the server rejects 0-RTT we need to drop a bunch of stuff.
fn client_0rtt_rejected(&mut self) {
fn client_0rtt_rejected(&mut self, now: Instant) {
if !matches!(self.zero_rtt_state, ZeroRttState::Sending) {
return;
}
qdebug!([self], "0-RTT rejected");
// Tell 0-RTT packets that they were "lost".
let dropped = self.loss_recovery.drop_0rtt(&self.paths.primary());
let dropped = self.loss_recovery.drop_0rtt(&self.paths.primary(), now);
self.handle_lost_packets(&dropped);
self.streams.zero_rtt_rejected();
@ -2651,14 +2846,15 @@ impl Connection {
self.zero_rtt_state = if self.crypto.tls.info().unwrap().early_data_accepted() {
ZeroRttState::AcceptedClient
} else {
self.client_0rtt_rejected();
self.client_0rtt_rejected(now);
ZeroRttState::Rejected
};
}
// Setting application keys has to occur after 0-RTT rejection.
let pto = self.pto();
self.crypto.install_application_keys(now + pto)?;
self.crypto
.install_application_keys(self.version, now + pto)?;
self.process_tps()?;
self.set_state(State::Connected);
self.create_resumption_token(now);
@ -2844,7 +3040,7 @@ impl Connection {
let (cspace, tx) = if let Some(crypto) = self
.crypto
.states
.select_tx(PacketNumberSpace::ApplicationData)
.select_tx(self.version, PacketNumberSpace::ApplicationData)
{
crypto
} else {

Просмотреть файл

@ -11,7 +11,8 @@ use crate::rtt::GRANULARITY;
use crate::stream_id::StreamType;
use crate::tparams::{self, PreferredAddress, TransportParameter, TransportParametersHandler};
use crate::tracking::DEFAULT_ACK_DELAY;
use crate::{CongestionControlAlgorithm, QuicVersion, Res};
use crate::version::{Version, VersionConfig};
use crate::{CongestionControlAlgorithm, Res};
use std::cmp::max;
use std::convert::TryFrom;
use std::time::Duration;
@ -42,9 +43,9 @@ pub enum PreferredAddressConfig {
/// ConnectionParameters use for setting intitial value for QUIC parameters.
/// This collects configuration like initial limits, protocol version, and
/// congestion control algorithm.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct ConnectionParameters {
quic_version: QuicVersion,
versions: VersionConfig,
cc_algorithm: CongestionControlAlgorithm,
/// Initial connection-level flow control limit.
max_data: u64,
@ -77,7 +78,7 @@ pub struct ConnectionParameters {
impl Default for ConnectionParameters {
fn default() -> Self {
Self {
quic_version: QuicVersion::default(),
versions: VersionConfig::default(),
cc_algorithm: CongestionControlAlgorithm::NewReno,
max_data: LOCAL_MAX_DATA,
max_stream_data_bidi_remote: u64::try_from(RECV_BUFFER_SIZE).unwrap(),
@ -97,12 +98,20 @@ impl Default for ConnectionParameters {
}
impl ConnectionParameters {
pub fn get_quic_version(&self) -> QuicVersion {
self.quic_version
pub fn get_versions(&self) -> &VersionConfig {
&self.versions
}
pub fn quic_version(mut self, v: QuicVersion) -> Self {
self.quic_version = v;
pub(crate) fn get_versions_mut(&mut self) -> &mut VersionConfig {
&mut self.versions
}
/// Describe the initial version that should be attempted and all the
/// versions that should be enabled. This list should contain the initial
/// version and be in order of preference, with more preferred versions
/// before less preferred.
pub fn versions(mut self, initial: Version, all: Vec<Version>) -> Self {
self.versions = VersionConfig::new(initial, all);
self
}
@ -278,7 +287,7 @@ impl ConnectionParameters {
role: Role,
cid_manager: &mut ConnectionIdManager,
) -> Res<TransportParametersHandler> {
let mut tps = TransportParametersHandler::default();
let mut tps = TransportParametersHandler::new(role, self.versions.clone());
// default parameters
tps.local.set_integer(
tparams::ACTIVE_CONNECTION_ID_LIMIT,

Просмотреть файл

@ -22,8 +22,17 @@ use crate::{ConnectionError, Error, Res};
#[derive(Clone, Debug, PartialEq, Eq)]
/// The state of the Connection.
pub enum State {
/// A newly created connection.
Init,
/// Waiting for the first Initial packet.
WaitInitial,
/// Waiting to confirm which version was selected.
/// For a client, this is confirmed when a CRYPTO frame is received;
/// the version of the packet determines the version.
/// For a server, this is confirmed when transport parameters are
/// received and processed.
WaitVersion,
/// Exchanging Handshake packets.
Handshaking,
Connected,
Confirmed,
@ -81,6 +90,8 @@ impl Ord for State {
(_, Self::Init) => Ordering::Greater,
(Self::WaitInitial, _) => Ordering::Less,
(_, Self::WaitInitial) => Ordering::Greater,
(Self::WaitVersion, _) => Ordering::Less,
(_, Self::WaitVersion) => Ordering::Greater,
(Self::Handshaking, _) => Ordering::Less,
(_, Self::Handshaking) => Ordering::Greater,
(Self::Connected, _) => Ordering::Less,

Просмотреть файл

@ -7,8 +7,8 @@
use super::super::{Connection, Output, State};
use super::{
assert_error, connect, connect_force_idle, connect_with_rtt, default_client, default_server,
get_tokens, handshake, maybe_authenticate, send_something, CountingConnectionIdGenerator,
AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA,
get_tokens, handshake, maybe_authenticate, resumed_server, send_something,
CountingConnectionIdGenerator, AT_LEAST_PTO, DEFAULT_RTT, DEFAULT_STREAM_DATA,
};
use crate::connection::AddressValidation;
use crate::events::ConnectionEvent;
@ -17,8 +17,7 @@ use crate::server::ValidateAddress;
use crate::tparams::{TransportParameter, MIN_ACK_DELAY};
use crate::tracking::DEFAULT_ACK_DELAY;
use crate::{
ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, QuicVersion,
StreamType,
ConnectionError, ConnectionParameters, EmptyConnectionIdGenerator, Error, StreamType, Version,
};
use neqo_common::{event::Provider, qdebug, Datagram};
@ -356,7 +355,7 @@ fn reorder_05rtt_with_0rtt() {
let token = get_tokens(&mut client).pop().unwrap();
let mut client = default_client();
client.enable_resumption(now, token).unwrap();
let mut server = default_server();
let mut server = resumed_server(&client);
// Send ClientHello and some 0-RTT.
let c1 = send_something(&mut client, now);
@ -398,7 +397,9 @@ fn reorder_05rtt_with_0rtt() {
now += RTT / 2;
server.process_input(c4.unwrap(), now);
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(server.paths.rtt(), RTT);
// Don't check server RTT as it will be massively inflated by a
// poor initial estimate received when the server dropped the
// Initial packet number space.
}
/// Test that a server that coalesces 0.5 RTT with handshake packets
@ -522,7 +523,9 @@ fn reorder_handshake() {
now += RTT / 2;
let s3 = server.process(c3, now).dgram();
assert_eq!(*server.state(), State::Confirmed);
assert_eq!(server.paths.rtt(), RTT);
// Don't check server RTT estimate as it will be inflated due to
// it making a guess based on retransmissions when it dropped
// the Initial packet number space.
now += RTT / 2;
client.process_input(s3.unwrap(), now);
@ -566,9 +569,8 @@ fn reorder_1rtt() {
now += RTT / 2;
let s2 = server.process(c2, now).dgram();
// The server has now received those packets, and saved them.
// The three additional are: an Initial ACK, a Handshake,
// and a 1-RTT (containing NEW_CONNECTION_ID).
assert_eq!(server.stats().packets_rx, PACKETS * 2 + 5);
// The two additional are a Handshake and a 1-RTT (w/ NEW_CONNECTION_ID).
assert_eq!(server.stats().packets_rx, PACKETS * 2 + 4);
assert_eq!(server.stats().saved_datagrams, PACKETS);
assert_eq!(server.stats().dropped_rx, 1);
assert_eq!(*server.state(), State::Confirmed);
@ -714,51 +716,36 @@ fn extra_initial_invalid_cid() {
assert!(nothing.is_none());
}
fn connect_version(version: QuicVersion) {
fixture_init();
let mut client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default().quic_version(version),
now(),
)
.unwrap();
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default().quic_version(version),
)
.unwrap();
connect_force_idle(&mut client, &mut server);
}
#[test]
fn connect_v1() {
connect_version(QuicVersion::Version1);
}
fn connect_one_version() {
fn connect_v(version: Version) {
fixture_init();
let mut client = Connection::new_client(
test_fixture::DEFAULT_SERVER_NAME,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
addr(),
addr(),
ConnectionParameters::default().versions(version, vec![version]),
now(),
)
.unwrap();
let mut server = Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default().versions(version, vec![version]),
)
.unwrap();
connect_force_idle(&mut client, &mut server);
assert_eq!(client.version(), version);
assert_eq!(server.version(), version);
}
#[test]
fn connect_29() {
connect_version(QuicVersion::Draft29);
}
#[test]
fn connect_30() {
connect_version(QuicVersion::Draft30);
}
#[test]
fn connect_31() {
connect_version(QuicVersion::Draft31);
}
#[test]
fn connect_32() {
connect_version(QuicVersion::Draft32);
for v in Version::all() {
println!("Connecting with {:?}", v);
connect_v(v);
}
}
#[test]
@ -797,9 +784,9 @@ fn anti_amplification() {
let ack = client.process(Some(s_init3), now).dgram().unwrap();
assert!(!maybe_authenticate(&mut client)); // No need yet.
// The client sends a padded datagram, with just ACK for Initial + Handshake.
assert_eq!(client.stats().frame_tx.ack, ack_count + 2);
assert_eq!(client.stats().frame_tx.all, frame_count + 2);
// The client sends a padded datagram, with just ACK for Handshake.
assert_eq!(client.stats().frame_tx.ack, ack_count + 1);
assert_eq!(client.stats().frame_tx.all, frame_count + 1);
assert_ne!(ack.len(), PATH_MTU_V6); // Not padded (it includes Handshake).
now += DEFAULT_RTT / 2;
@ -1028,3 +1015,115 @@ fn bad_min_ack_delay() {
)))
);
}
/// Ensure that the client probes correctly if it only receives Initial packets
/// from the server.
#[test]
fn only_server_initial() {
let mut server = default_server();
let mut client = default_client();
let mut now = now();
let client_dgram = client.process_output(now).dgram();
// Now fetch two flights of messages from the server.
let server_dgram1 = server.process(client_dgram, now).dgram();
let server_dgram2 = server.process_output(now + AT_LEAST_PTO).dgram();
// Only pass on the Initial from the first. We should get a Handshake in return.
let (initial, handshake) = split_datagram(&server_dgram1.unwrap());
assert!(handshake.is_some());
// The client will not acknowledge the Initial as it discards keys.
// It sends a Handshake probe instead, containing just a PING frame.
assert_eq!(client.stats().frame_tx.ping, 0);
let probe = client.process(Some(initial), now).dgram();
assertions::assert_handshake(&probe.unwrap());
assert_eq!(client.stats().dropped_rx, 0);
assert_eq!(client.stats().frame_tx.ping, 1);
let (initial, handshake) = split_datagram(&server_dgram2.unwrap());
assert!(handshake.is_some());
// The same happens after a PTO, even though the client will discard the Initial packet.
now += AT_LEAST_PTO;
assert_eq!(client.stats().frame_tx.ping, 1);
let discarded = client.stats().dropped_rx;
let probe = client.process(Some(initial), now).dgram();
assertions::assert_handshake(&probe.unwrap());
assert_eq!(client.stats().frame_tx.ping, 2);
assert_eq!(client.stats().dropped_rx, discarded + 1);
// Pass the Handshake packet and complete the handshake.
client.process_input(handshake.unwrap(), now);
maybe_authenticate(&mut client);
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
client.process_input(dgram.unwrap(), now);
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(*server.state(), State::Confirmed);
}
// Collect a few spare Initial packets as the handshake is exchanged.
// Later, replay those packets to see if they result in additional probes; they should not.
#[test]
fn no_extra_probes_after_confirmed() {
let mut server = default_server();
let mut client = default_client();
let mut now = now();
// First, collect a client Initial.
let spare_initial = client.process_output(now).dgram();
assert!(spare_initial.is_some());
// Collect ANOTHER client Initial.
now += AT_LEAST_PTO;
let dgram = client.process_output(now).dgram();
let (replay_initial, _) = split_datagram(dgram.as_ref().unwrap());
// Finally, run the handshake.
now += AT_LEAST_PTO * 2;
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
// The server should have dropped the Initial keys now, so passing in the Initial
// should elicit a retransmit rather than having it completely ignored.
let spare_handshake = server.process(Some(replay_initial), now).dgram();
assert!(spare_handshake.is_some());
client.process_input(dgram.unwrap(), now);
maybe_authenticate(&mut client);
let dgram = client.process_output(now).dgram();
let dgram = server.process(dgram, now).dgram();
client.process_input(dgram.unwrap(), now);
assert_eq!(*client.state(), State::Confirmed);
assert_eq!(*server.state(), State::Confirmed);
let probe = server.process(spare_initial, now).dgram();
assert!(probe.is_none());
let probe = client.process(spare_handshake, now).dgram();
assert!(probe.is_none());
}
#[test]
fn implicit_rtt_server() {
const RTT: Duration = Duration::from_secs(2);
let mut server = default_server();
let mut client = default_client();
let mut now = now();
let dgram = client.process_output(now).dgram();
now += RTT / 2;
let dgram = server.process(dgram, now).dgram();
now += RTT / 2;
let dgram = client.process(dgram, now).dgram();
assertions::assert_handshake(dgram.as_ref().unwrap());
now += RTT / 2;
server.process_input(dgram.unwrap(), now);
// The server doesn't receive any acknowledgments, but it can infer
// an RTT estimate from having discarded the Initial packet number space.
assert_eq!(server.stats().rtt, RTT);
}

Просмотреть файл

@ -18,13 +18,19 @@ use neqo_common::{qdebug, Datagram};
use std::mem;
use test_fixture::{self, now};
fn check_discarded(peer: &mut Connection, pkt: Datagram, dropped: usize, dups: usize) {
fn check_discarded(
peer: &mut Connection,
pkt: Datagram,
response: bool,
dropped: usize,
dups: usize,
) {
// Make sure to flush any saved datagrams before doing this.
mem::drop(peer.process_output(now()));
let before = peer.stats();
let out = peer.process(Some(pkt), now());
assert!(out.as_dgram_ref().is_none());
assert_eq!(out.as_dgram_ref().is_some(), response);
let after = peer.stats();
assert_eq!(dropped, after.dropped_rx - before.dropped_rx);
assert_eq!(dups, after.dups_rx - before.dups_rx);
@ -60,11 +66,12 @@ fn discarded_initial_keys() {
let out = client.process(init_pkt_s.clone(), now()).dgram();
assert!(out.is_some());
// The client has received handshake packet. It will remove the Initial keys.
// The client has received a handshake packet. It will remove the Initial keys.
// We will check this by processing init_pkt_s a second time.
// The initial packet should be dropped. The packet contains a Handshake packet as well, which
// will be marked as dup. And it will contain padding, which will be "dropped".
check_discarded(&mut client, init_pkt_s.unwrap(), 2, 1);
// The client will generate a Handshake packet here to avoid stalling.
check_discarded(&mut client, init_pkt_s.unwrap(), true, 2, 1);
assert!(maybe_authenticate(&mut client));
@ -72,7 +79,7 @@ fn discarded_initial_keys() {
// packet from the client.
// We will check this by processing init_pkt_c a second time.
// The dropped packet is padding. The Initial packet has been mark dup.
check_discarded(&mut server, init_pkt_c.clone().unwrap(), 1, 1);
check_discarded(&mut server, init_pkt_c.clone().unwrap(), false, 1, 1);
qdebug!("---- client: SH..FIN -> FIN");
let out = client.process(None, now()).dgram();
@ -87,7 +94,7 @@ fn discarded_initial_keys() {
// We will check this by processing init_pkt_c a third time.
// The Initial packet has been dropped and padding that follows it.
// There is no dups, everything has been dropped.
check_discarded(&mut server, init_pkt_c.unwrap(), 1, 0);
check_discarded(&mut server, init_pkt_c.unwrap(), false, 1, 0);
}
#[test]
@ -200,7 +207,7 @@ fn key_update_consecutive() {
// However, as the server didn't wait long enough to update again, the
// client hasn't rotated its keys, so the packet gets dropped.
check_discarded(&mut client, dgram, 1, 0);
check_discarded(&mut client, dgram, false, 1, 0);
}
// Key updates can't be initiated too early.

Просмотреть файл

@ -16,6 +16,7 @@ use crate::recovery::ACK_ONLY_SIZE_LIMIT;
use crate::stats::{FrameStats, Stats, MAX_PTO_COUNTS};
use crate::{
ConnectionIdDecoder, ConnectionIdGenerator, ConnectionParameters, Error, StreamId, StreamType,
Version,
};
use std::cell::RefCell;
@ -128,6 +129,9 @@ pub fn new_server(params: ConnectionParameters) -> Connection {
pub fn default_server() -> Connection {
new_server(ConnectionParameters::default())
}
pub fn resumed_server(client: &Connection) -> Connection {
new_server(ConnectionParameters::default().versions(client.version(), Version::all()))
}
/// If state is `AuthenticationNeeded` call `authenticated()`. This function will
/// consume all outstanding events on the connection.
@ -211,10 +215,10 @@ fn connect(client: &mut Connection, server: &mut Connection) {
connect_with_rtt(client, server, now(), Duration::new(0, 0));
}
fn assert_error(c: &Connection, err: &ConnectionError) {
fn assert_error(c: &Connection, expected: &ConnectionError) {
match c.state() {
State::Closing { error, .. } | State::Draining { error, .. } | State::Closed(error) => {
assert_eq!(*error, *err);
assert_eq!(*error, *expected, "{} error mismatch", c);
}
_ => panic!("bad state {:?}", c.state()),
}

Просмотреть файл

@ -6,9 +6,10 @@
use super::{
connect, connect_with_rtt, default_client, default_server, exchange_ticket, get_tokens,
send_something, AT_LEAST_PTO,
new_client, resumed_server, send_something, AT_LEAST_PTO,
};
use crate::addr_valid::{AddressValidation, ValidateAddress};
use crate::{ConnectionParameters, Error, Version};
use std::cell::RefCell;
use std::mem;
@ -27,7 +28,7 @@ fn resume() {
client
.enable_resumption(now(), token)
.expect("should set token");
let mut server = default_server();
let mut server = resumed_server(&client);
connect(&mut client, &mut server);
assert!(client.tls_info().unwrap().resumed());
assert!(server.tls_info().unwrap().resumed());
@ -58,13 +59,13 @@ fn remember_smoothed_rtt() {
let token = get_tokens(&mut client).pop().unwrap();
let mut client = default_client();
let mut server = default_server();
client.enable_resumption(now, token).unwrap();
assert_eq!(
client.paths.rtt(),
RTT1,
"client should remember previous RTT"
);
let mut server = resumed_server(&client);
connect_with_rtt(&mut client, &mut server, now, RTT2);
assert_eq!(
@ -89,7 +90,7 @@ fn address_validation_token_resume() {
let token = exchange_ticket(&mut client, &mut server, now);
let mut client = default_client();
client.enable_resumption(now, token).unwrap();
let mut server = default_server();
let mut server = resumed_server(&client);
// Grab an Initial packet from the client.
let dgram = client.process(None, now).dgram();
@ -193,3 +194,53 @@ fn take_token() {
let token = client.take_resumption_token(now()).unwrap();
can_resume(&token, false);
}
/// If a version is selected and subsequently disabled, resumption fails.
#[test]
fn resume_disabled_version() {
let mut client = new_client(
ConnectionParameters::default().versions(Version::Version1, vec![Version::Version1]),
);
let mut server = default_server();
connect(&mut client, &mut server);
let token = exchange_ticket(&mut client, &mut server, now());
let mut client = new_client(
ConnectionParameters::default().versions(Version::Version2, vec![Version::Version2]),
);
assert_eq!(
client.enable_resumption(now(), token).unwrap_err(),
Error::DisabledVersion
);
}
/// It's not possible to resume once a packet has been sent.
#[test]
fn resume_after_packet() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
let token = exchange_ticket(&mut client, &mut server, now());
let mut client = default_client();
mem::drop(client.process_output(now()).dgram().unwrap());
assert_eq!(
client.enable_resumption(now(), token).unwrap_err(),
Error::ConnectionState
);
}
/// It's not possible to resume at the server.
#[test]
fn resume_server() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
let token = exchange_ticket(&mut client, &mut server, now());
let mut server = default_server();
assert_eq!(
server.enable_resumption(now(), token).unwrap_err(),
Error::ConnectionState
);
}

Просмотреть файл

@ -4,15 +4,19 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::super::{ConnectionError, Output, State};
use super::{default_client, default_server};
use super::super::{ConnectionError, ConnectionEvent, Output, State, ZeroRttState};
use super::{
connect, connect_fail, default_client, default_server, exchange_ticket, new_client, new_server,
send_something,
};
use crate::packet::PACKET_BIT_LONG;
use crate::{Error, QuicVersion};
use crate::tparams::{self, TransportParameter};
use crate::{ConnectionParameters, Error, Version};
use neqo_common::{Datagram, Decoder, Encoder};
use neqo_common::{event::Provider, Datagram, Decoder, Encoder};
use std::mem;
use std::time::Duration;
use test_fixture::{self, addr, now};
use test_fixture::{self, addr, assertions, now};
// The expected PTO duration after the first Initial is sent.
const INITIAL_PTO: Duration = Duration::from_millis(300);
@ -58,8 +62,8 @@ fn create_vn(initial_pkt: &[u8], versions: &[u32]) -> Vec<u8> {
let mut encoder = Encoder::default();
encoder.encode_byte(PACKET_BIT_LONG);
encoder.encode(&[0; 4]); // Zero version == VN.
encoder.encode_vec(1, dst_cid);
encoder.encode_vec(1, src_cid);
encoder.encode_vec(1, dst_cid);
for v in versions {
encoder.encode_uint(4, *v);
@ -79,7 +83,7 @@ fn version_negotiation_current_version() {
let vn = create_vn(
&initial_pkt,
&[0x1a1a_1a1a, QuicVersion::default().as_u32()],
&[0x1a1a_1a1a, Version::default().wire_version()],
);
let dgram = Datagram::new(addr(), addr(), vn);
@ -89,6 +93,25 @@ fn version_negotiation_current_version() {
assert_eq!(1, client.stats().dropped_rx);
}
#[test]
fn version_negotiation_version0() {
let mut client = default_client();
// Start the handshake.
let initial_pkt = client
.process(None, now())
.dgram()
.expect("a datagram")
.to_vec();
let vn = create_vn(&initial_pkt, &[0, 0x1a1a_1a1a]);
let dgram = Datagram::new(addr(), addr(), vn);
let delay = client.process(Some(dgram), now()).callback();
assert_eq!(delay, INITIAL_PTO);
assert_eq!(*client.state(), State::WaitInitial);
assert_eq!(1, client.stats().dropped_rx);
}
#[test]
fn version_negotiation_only_reserved() {
let mut client = default_client();
@ -160,11 +183,8 @@ fn version_negotiation_not_supported() {
.to_vec();
let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]);
assert_eq!(
client.process(Some(Datagram::new(addr(), addr(), vn)), now(),),
Output::None
);
let dgram = Datagram::new(addr(), addr(), vn);
assert_eq!(client.process(Some(dgram), now()), Output::None);
match client.state() {
State::Closed(err) => {
assert_eq!(*err, ConnectionError::Transport(Error::VersionNegotiation));
@ -177,14 +197,14 @@ fn version_negotiation_not_supported() {
fn version_negotiation_bad_cid() {
let mut client = default_client();
// Start the handshake.
let initial_pkt = client
let mut initial_pkt = client
.process(None, now())
.dgram()
.expect("a datagram")
.to_vec();
let mut vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]);
vn[6] ^= 0xc4;
initial_pkt[6] ^= 0xc4;
let vn = create_vn(&initial_pkt, &[0x1a1a_1a1a, 0x2a2a_2a2a, 0xff00_0001]);
let dgram = Datagram::new(addr(), addr(), vn);
let delay = client.process(Some(dgram), now()).callback();
@ -192,3 +212,275 @@ fn version_negotiation_bad_cid() {
assert_eq!(*client.state(), State::WaitInitial);
assert_eq!(1, client.stats().dropped_rx);
}
#[test]
fn compatible_upgrade() {
let mut client = default_client();
let mut server = default_server();
connect(&mut client, &mut server);
assert_eq!(client.version(), Version::Version2);
assert_eq!(server.version(), Version::Version2);
}
/// When the first packet from the client is gigantic, the server might generate acknowledgment packets in
/// version 1. Both client and server need to handle that gracefully.
#[test]
fn compatible_upgrade_large_initial() {
let params = ConnectionParameters::default().versions(
Version::Version1,
vec![Version::Version2, Version::Version1],
);
let mut client = new_client(params.clone());
client
.set_local_tparam(
0x0845_de37_00ac_a5f9,
TransportParameter::Bytes(vec![0; 2048]),
)
.unwrap();
let mut server = new_server(params);
// Client Initial should take 2 packets.
// Each should elicit a Version 1 ACK from the server.
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
let dgram = server.process(dgram, now()).dgram();
assert!(dgram.is_some());
// The following uses the Version from *outside* this crate.
assertions::assert_version(dgram.as_ref().unwrap(), Version::Version1.wire_version());
client.process_input(dgram.unwrap(), now());
connect(&mut client, &mut server);
assert_eq!(client.version(), Version::Version2);
assert_eq!(server.version(), Version::Version2);
// Only handshake padding is "dropped".
assert_eq!(client.stats().dropped_rx, 1);
assert_eq!(server.stats().dropped_rx, 1);
}
/// A server that supports versions 1 and 2 might prefer version 1 and that's OK.
/// This one starts with version 1 and stays there.
#[test]
fn compatible_no_upgrade() {
let mut client = new_client(ConnectionParameters::default().versions(
Version::Version1,
vec![Version::Version2, Version::Version1],
));
let mut server = new_server(ConnectionParameters::default().versions(
Version::Version1,
vec![Version::Version1, Version::Version2],
));
connect(&mut client, &mut server);
assert_eq!(client.version(), Version::Version1);
assert_eq!(server.version(), Version::Version1);
}
/// A server that supports versions 1 and 2 might prefer version 1 and that's OK.
/// This one starts with version 2 and downgrades to version 1.
#[test]
fn compatible_downgrade() {
let mut client = new_client(ConnectionParameters::default().versions(
Version::Version2,
vec![Version::Version2, Version::Version1],
));
let mut server = new_server(ConnectionParameters::default().versions(
Version::Version2,
vec![Version::Version1, Version::Version2],
));
connect(&mut client, &mut server);
assert_eq!(client.version(), Version::Version1);
assert_eq!(server.version(), Version::Version1);
}
/// Inject a Version Negotiation packet, which the client detects when it validates the
/// server `version_negotiation` transport parameter.
#[test]
fn version_negotiation_downgrade() {
const DOWNGRADE: Version = Version::Draft29;
let mut client = default_client();
// The server sets the current version in the transport parameter and
// protects Initial packets with the version in its configuration.
// When a server `Connection` is created by a `Server`, the configuration is set
// to match the version of the packet it first receives. This replicates that.
let mut server =
new_server(ConnectionParameters::default().versions(DOWNGRADE, Version::all()));
// Start the handshake and spoof a VN packet.
let initial = client.process_output(now()).dgram().unwrap();
let vn = create_vn(&initial, &[DOWNGRADE.wire_version()]);
let dgram = Datagram::new(addr(), addr(), vn);
client.process_input(dgram, now());
connect_fail(
&mut client,
&mut server,
Error::VersionNegotiation,
Error::PeerError(Error::VersionNegotiation.code()),
);
}
/// A server connection needs to be configured with the version that the client attempts.
/// Otherwise, it will object to the client transport parameters and not do anything.
#[test]
fn invalid_server_version() {
let mut client =
new_client(ConnectionParameters::default().versions(Version::Version1, Version::all()));
let mut server =
new_server(ConnectionParameters::default().versions(Version::Version2, Version::all()));
let dgram = client.process_output(now()).dgram();
server.process_input(dgram.unwrap(), now());
// One packet received.
assert_eq!(server.stats().packets_rx, 1);
// None dropped; the server will have decrypted it successfully.
assert_eq!(server.stats().dropped_rx, 0);
assert_eq!(server.stats().saved_datagrams, 0);
// The server effectively hasn't reacted here.
match server.state() {
State::Closed(err) => {
assert_eq!(*err, ConnectionError::Transport(Error::CryptoAlert(47)));
}
_ => panic!("invalid server state"),
}
}
#[test]
fn invalid_current_version_client() {
const OTHER_VERSION: Version = Version::Draft29;
let mut client = default_client();
let mut server = default_server();
assert_ne!(OTHER_VERSION, client.version());
client
.set_local_tparam(
tparams::VERSION_NEGOTIATION,
TransportParameter::Versions {
current: OTHER_VERSION.wire_version(),
other: Version::all()
.iter()
.copied()
.map(Version::wire_version)
.collect(),
},
)
.unwrap();
connect_fail(
&mut client,
&mut server,
Error::PeerError(Error::CryptoAlert(47).code()),
Error::CryptoAlert(47),
);
}
/// To test this, we need to disable compatible upgrade so that the server doesn't update
/// its transport parameters. Then, we can overwrite its transport parameters without
/// them being overwritten. Otherwise, it would be hard to find a window during which
/// the transport parameter can be modified.
#[test]
fn invalid_current_version_server() {
const OTHER_VERSION: Version = Version::Draft29;
let mut client = default_client();
let mut server = new_server(
ConnectionParameters::default().versions(Version::default(), vec![Version::default()]),
);
assert!(!Version::default().is_compatible(OTHER_VERSION));
server
.set_local_tparam(
tparams::VERSION_NEGOTIATION,
TransportParameter::Versions {
current: OTHER_VERSION.wire_version(),
other: vec![OTHER_VERSION.wire_version()],
},
)
.unwrap();
connect_fail(
&mut client,
&mut server,
Error::CryptoAlert(47),
Error::PeerError(Error::CryptoAlert(47).code()),
);
}
#[test]
fn no_compatible_version() {
const OTHER_VERSION: Version = Version::Draft29;
let mut client = default_client();
let mut server = default_server();
assert_ne!(OTHER_VERSION, client.version());
client
.set_local_tparam(
tparams::VERSION_NEGOTIATION,
TransportParameter::Versions {
current: Version::default().wire_version(),
other: vec![OTHER_VERSION.wire_version()],
},
)
.unwrap();
connect_fail(
&mut client,
&mut server,
Error::PeerError(Error::CryptoAlert(47).code()),
Error::CryptoAlert(47),
);
}
/// When a compatible upgrade chooses a different version, 0-RTT is rejected.
#[test]
fn compatible_upgrade_0rtt_rejected() {
// This is the baseline configuration where v1 is attempted and v2 preferred.
let prefer_v2 = ConnectionParameters::default().versions(
Version::Version1,
vec![Version::Version2, Version::Version1],
);
let mut client = new_client(prefer_v2.clone());
// The server will start with this so that the client resumes with v1.
let just_v1 =
ConnectionParameters::default().versions(Version::Version1, vec![Version::Version1]);
let mut server = new_server(just_v1);
connect(&mut client, &mut server);
assert_eq!(client.version(), Version::Version1);
let token = exchange_ticket(&mut client, &mut server, now());
// Now upgrade the server to the preferred configuration.
let mut client = new_client(prefer_v2.clone());
let mut server = new_server(prefer_v2);
client.enable_resumption(now(), token).unwrap();
// Create a packet with 0-RTT from the client.
let initial = send_something(&mut client, now());
assertions::assert_version(&initial, Version::Version1.wire_version());
assertions::assert_coalesced_0rtt(&initial);
server.process_input(initial, now());
assert!(!server
.events()
.any(|e| matches!(e, ConnectionEvent::NewStream { .. })));
// Finalize the connection. Don't use connect() because it uses
// maybe_authenticate() too liberally and that eats the events we want to check.
let dgram = server.process_output(now()).dgram(); // ServerHello flight
let dgram = client.process(dgram, now()).dgram(); // Client Finished (note: no authentication)
let dgram = server.process(dgram, now()).dgram(); // HANDSHAKE_DONE
client.process_input(dgram.unwrap(), now());
assert!(matches!(client.state(), State::Confirmed));
assert!(matches!(server.state(), State::Confirmed));
assert!(client.events().any(|e| {
println!(" client event: {:?}", e);
matches!(e, ConnectionEvent::ZeroRttRejected)
}));
assert_eq!(client.zero_rtt_state(), ZeroRttState::Rejected);
}

Просмотреть файл

@ -6,11 +6,11 @@
use super::super::Connection;
use super::{
connect, default_client, default_server, exchange_ticket, new_server,
connect, default_client, default_server, exchange_ticket, new_server, resumed_server,
CountingConnectionIdGenerator,
};
use crate::events::ConnectionEvent;
use crate::{ConnectionParameters, Error, StreamType};
use crate::{ConnectionParameters, Error, StreamType, Version};
use neqo_common::event::Provider;
use neqo_crypto::{AllowZeroRtt, AntiReplay};
@ -31,7 +31,7 @@ fn zero_rtt_negotiate() {
client
.enable_resumption(now(), token)
.expect("should set token");
let mut server = default_server();
let mut server = resumed_server(&client);
connect(&mut client, &mut server);
assert!(client.tls_info().unwrap().early_data_accepted());
assert!(server.tls_info().unwrap().early_data_accepted());
@ -48,7 +48,7 @@ fn zero_rtt_send_recv() {
client
.enable_resumption(now(), token)
.expect("should set token");
let mut server = default_server();
let mut server = resumed_server(&client);
// Send ClientHello.
let client_hs = client.process(None, now());
@ -93,7 +93,7 @@ fn zero_rtt_send_coalesce() {
client
.enable_resumption(now(), token)
.expect("should set token");
let mut server = default_server();
let mut server = resumed_server(&client);
// Write 0-RTT before generating any packets.
// This should result in a datagram that coalesces Initial and 0-RTT.
@ -140,7 +140,7 @@ fn zero_rtt_send_reject() {
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default(),
ConnectionParameters::default().versions(client.version(), Version::all()),
)
.unwrap();
// Using a freshly initialized anti-replay context
@ -221,7 +221,8 @@ fn zero_rtt_update_flow_control() {
let mut server = new_server(
ConnectionParameters::default()
.max_stream_data(StreamType::UniDi, true, HIGH)
.max_stream_data(StreamType::BiDi, true, HIGH),
.max_stream_data(StreamType::BiDi, true, HIGH)
.versions(client.version, Version::all()),
);
// Stream limits should be low for 0-RTT.

294
third_party/rust/neqo-transport/src/crypto.rs поставляемый
Просмотреть файл

@ -6,6 +6,7 @@
use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::mem;
use std::ops::{Index, IndexMut, Range};
@ -22,13 +23,15 @@ use neqo_crypto::{
TLS_VERSION_1_3,
};
use crate::packet::{PacketBuilder, PacketNumber, QuicVersion};
use crate::cid::ConnectionIdRef;
use crate::packet::{PacketBuilder, PacketNumber};
use crate::recovery::RecoveryToken;
use crate::recv_stream::RxStreamOrderer;
use crate::send_stream::TxBuffer;
use crate::stats::FrameStats;
use crate::tparams::{TpZeroRttChecker, TransportParameters, TransportParametersHandler};
use crate::tracking::PacketNumberSpace;
use crate::version::Version;
use crate::{Error, Res};
const MAX_AUTH_TAG: usize = 32;
@ -48,6 +51,8 @@ thread_local!(pub(crate) static OVERWRITE_INVOCATIONS: RefCell<Option<PacketNumb
#[derive(Debug)]
pub struct Crypto {
version: Version,
protocols: Vec<String>,
pub(crate) tls: Agent,
pub(crate) streams: CryptoStreams,
pub(crate) states: CryptoStates,
@ -57,9 +62,9 @@ type TpHandler = Rc<RefCell<TransportParametersHandler>>;
impl Crypto {
pub fn new(
version: QuicVersion,
version: Version,
mut agent: Agent,
protocols: &[impl AsRef<str>],
protocols: Vec<String>,
tphandler: TpHandler,
) -> Res<Self> {
agent.set_version_range(TLS_VERSION_1_3, TLS_VERSION_1_3)?;
@ -68,7 +73,7 @@ impl Crypto {
TLS_AES_256_GCM_SHA384,
TLS_CHACHA20_POLY1305_SHA256,
])?;
agent.set_alpn(protocols)?;
agent.set_alpn(&protocols)?;
agent.disable_end_of_early_data()?;
// Always enable 0-RTT on the client, but the server needs
// more configuration passed to server_enable_0rtt.
@ -76,20 +81,33 @@ impl Crypto {
c.enable_0rtt()?;
}
let extension = match version {
QuicVersion::Version1 => 0x39,
QuicVersion::Draft29
| QuicVersion::Draft30
| QuicVersion::Draft31
| QuicVersion::Draft32 => 0xffa5,
Version::Version2 | Version::Version1 => 0x39,
Version::Draft29 | Version::Draft30 | Version::Draft31 | Version::Draft32 => 0xffa5,
};
agent.extension_handler(extension, tphandler)?;
Ok(Self {
version,
protocols,
tls: agent,
streams: Default::default(),
states: Default::default(),
})
}
/// Get the name of the server. (Only works for the client currently).
pub fn server_name(&self) -> Option<&str> {
if let Agent::Client(c) = &self.tls {
Some(c.server_name())
} else {
None
}
}
/// Get the set of enabled protocols.
pub fn protocols(&self) -> &[String] {
&self.protocols
}
pub fn server_enable_0rtt(
&mut self,
tphandler: TpHandler,
@ -174,7 +192,7 @@ impl Crypto {
}
/// Enable 0-RTT and return `true` if it is enabled successfully.
pub fn enable_0rtt(&mut self, role: Role) -> Res<bool> {
pub fn enable_0rtt(&mut self, version: Version, role: Role) -> Res<bool> {
let info = self.tls.preinfo()?;
// `info.early_data()` returns false for a server,
// so use `early_data_cipher()` to tell if 0-RTT is enabled.
@ -193,16 +211,23 @@ impl Crypto {
),
};
let secret = secret.ok_or(Error::InternalError(1))?;
self.states.set_0rtt_keys(dir, &secret, cipher.unwrap());
self.states
.set_0rtt_keys(version, dir, &secret, cipher.unwrap());
Ok(true)
}
/// Lock in a compatible upgrade.
pub fn confirm_version(&mut self, confirmed: Version) {
self.states.confirm_version(self.version, confirmed);
self.version = confirmed;
}
/// Returns true if new handshake keys were installed.
pub fn install_keys(&mut self, role: Role) -> Res<bool> {
if !self.tls.state().is_final() {
let installed_hs = self.install_handshake_keys()?;
if role == Role::Server {
self.maybe_install_application_write_key()?;
self.maybe_install_application_write_key(self.version)?;
}
Ok(installed_hs)
} else {
@ -228,22 +253,22 @@ impl Crypto {
}
.ok_or(Error::InternalError(3))?;
self.states
.set_handshake_keys(&write_secret, &read_secret, cipher);
.set_handshake_keys(self.version, &write_secret, &read_secret, cipher);
qdebug!([self], "Handshake keys installed");
Ok(true)
}
fn maybe_install_application_write_key(&mut self) -> Res<()> {
fn maybe_install_application_write_key(&mut self, version: Version) -> Res<()> {
qtrace!([self], "Attempt to install application write key");
if let Some(secret) = self.tls.write_secret(TLS_EPOCH_APPLICATION_DATA) {
self.states.set_application_write_key(secret)?;
self.states.set_application_write_key(version, secret)?;
qdebug!([self], "Application write key installed");
}
Ok(())
}
pub fn install_application_keys(&mut self, expire_0rtt: Instant) -> Res<()> {
self.maybe_install_application_write_key()?;
pub fn install_application_keys(&mut self, version: Version, expire_0rtt: Instant) -> Res<()> {
self.maybe_install_application_write_key(version)?;
// The write key might have been installed earlier, but it should
// always be installed now.
debug_assert!(self.states.app_write.is_some());
@ -252,7 +277,7 @@ impl Crypto {
.read_secret(TLS_EPOCH_APPLICATION_DATA)
.ok_or(Error::InternalError(4))?;
self.states
.set_application_read_key(read_secret, expire_0rtt)?;
.set_application_read_key(version, read_secret, expire_0rtt)?;
qdebug!([self], "application read keys installed");
Ok(())
}
@ -316,19 +341,21 @@ impl Crypto {
&mut self,
new_token: Option<&[u8]>,
tps: &TransportParameters,
version: Version,
rtt: u64,
) -> Option<ResumptionToken> {
if let Agent::Client(ref mut c) = self.tls {
if let Some(ref t) = c.resumption_token() {
qtrace!("TLS token {}", hex(t.as_ref()));
let mut enc = Encoder::default();
enc.encode_uint(4, version.wire_version());
enc.encode_varint(rtt);
enc.encode_vvec_with(|enc_inner| {
tps.encode(enc_inner);
});
enc.encode_vvec(new_token.unwrap_or(&[]));
enc.encode(t.as_ref());
qinfo!("resumption token {}", hex_snip_middle(&enc[..]));
qinfo!("resumption token {}", hex_snip_middle(enc.as_ref()));
Some(ResumptionToken::new(enc.into(), t.expiration_time()))
} else {
None
@ -361,6 +388,9 @@ pub enum CryptoDxDirection {
#[derive(Debug)]
pub struct CryptoDxState {
/// The QUIC version.
version: Version,
/// Whether packets protected with this state will be read or written.
direction: CryptoDxDirection,
/// The epoch of this crypto state. This initially tracks TLS epochs
/// via DTLS: 0 = initial, 1 = 0-RTT, 2 = handshake, 3 = application.
@ -383,22 +413,26 @@ pub struct CryptoDxState {
impl CryptoDxState {
#[allow(clippy::reversed_empty_ranges)] // To initialize an empty range.
pub fn new(
version: Version,
direction: CryptoDxDirection,
epoch: Epoch,
secret: &SymKey,
cipher: Cipher,
) -> Self {
qinfo!(
"Making {:?} {} CryptoDxState, cipher={}",
"Making {:?} {} CryptoDxState, v={:?} cipher={}",
direction,
epoch,
cipher
version,
cipher,
);
let hplabel = String::from(version.label_prefix()) + "hp";
Self {
version,
direction,
epoch: usize::from(epoch),
aead: Aead::new(TLS_VERSION_1_3, cipher, secret, "quic ").unwrap(),
hpkey: HpKey::extract(TLS_VERSION_1_3, cipher, secret, "quic hp").unwrap(),
aead: Aead::new(TLS_VERSION_1_3, cipher, secret, version.label_prefix()).unwrap(),
hpkey: HpKey::extract(TLS_VERSION_1_3, cipher, secret, &hplabel).unwrap(),
used_pn: 0..0,
min_pn: 0,
invocations: Self::limit(direction, cipher),
@ -406,27 +440,13 @@ impl CryptoDxState {
}
pub fn new_initial(
quic_version: QuicVersion,
version: Version,
direction: CryptoDxDirection,
label: &str,
dcid: &[u8],
) -> Self {
const INITIAL_SALT_V1: &[u8] = &[
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
];
const INITIAL_SALT_29_32: &[u8] = &[
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
];
qtrace!("new_initial for {:?}", quic_version);
let salt = match quic_version {
QuicVersion::Version1 => INITIAL_SALT_V1,
QuicVersion::Draft29
| QuicVersion::Draft30
| QuicVersion::Draft31
| QuicVersion::Draft32 => INITIAL_SALT_29_32,
};
qtrace!("new_initial {:?} {}", version, ConnectionIdRef::from(dcid));
let salt = version.initial_salt();
let cipher = TLS_AES_128_GCM_SHA256;
let initial_secret = hkdf::extract(
TLS_VERSION_1_3,
@ -439,7 +459,7 @@ impl CryptoDxState {
let secret =
hkdf::expand_label(TLS_VERSION_1_3, cipher, &initial_secret, &[], label).unwrap();
Self::new(direction, TLS_EPOCH_INITIAL, &secret, cipher)
Self::new(version, direction, TLS_EPOCH_INITIAL, &secret, cipher)
}
/// Determine the confidentiality and integrity limits for the cipher.
@ -495,9 +515,16 @@ impl CryptoDxState {
Self::limit(CryptoDxDirection::Write, cipher)
};
Self {
version: self.version,
direction: self.direction,
epoch: self.epoch + 1,
aead: Aead::new(TLS_VERSION_1_3, cipher, next_secret, "quic ").unwrap(),
aead: Aead::new(
TLS_VERSION_1_3,
cipher,
next_secret,
self.version.label_prefix(),
)
.unwrap(),
hpkey: self.hpkey.clone(),
used_pn: pn..pn,
min_pn: pn,
@ -505,6 +532,11 @@ impl CryptoDxState {
}
}
#[must_use]
pub fn version(&self) -> Version {
self.version
}
#[must_use]
pub fn key_phase(&self) -> bool {
// Epoch 3 => 0, 4 => 1, 5 => 0, 6 => 1, ...
@ -517,8 +549,7 @@ impl CryptoDxState {
debug_assert_eq!(self.direction, prev.direction);
let next = prev.next_pn();
self.min_pn = next;
// TODO(mt) use Range::is_empty() when available
if self.used_pn.start == self.used_pn.end {
if self.used_pn.is_empty() {
self.used_pn = next..next;
Ok(())
} else if prev.used_pn.end > self.used_pn.start {
@ -638,7 +669,7 @@ impl CryptoDxState {
// This matches the value in packet.rs
const CLIENT_CID: &[u8] = &[0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08];
Self::new_initial(
QuicVersion::default(),
Version::default(),
CryptoDxDirection::Write,
"server in",
CLIENT_CID,
@ -698,9 +729,14 @@ pub(crate) struct CryptoDxAppData {
}
impl CryptoDxAppData {
pub fn new(dir: CryptoDxDirection, secret: SymKey, cipher: Cipher) -> Res<Self> {
pub fn new(
version: Version,
dir: CryptoDxDirection,
secret: SymKey,
cipher: Cipher,
) -> Res<Self> {
Ok(Self {
dx: CryptoDxState::new(dir, TLS_EPOCH_APPLICATION_DATA, &secret, cipher),
dx: CryptoDxState::new(version, dir, TLS_EPOCH_APPLICATION_DATA, &secret, cipher),
cipher,
next_secret: Self::update_secret(cipher, &secret)?,
})
@ -737,9 +773,14 @@ pub enum CryptoSpace {
ApplicationData,
}
/// All of the keying material needed for a connection.
///
/// Note that the methods on this struct take a version but those are only ever
/// used for Initial keys; a version has been selected at the time we need to
/// get other keys, so those have fixed versions.
#[derive(Debug, Default)]
pub struct CryptoStates {
initial: Option<CryptoState>,
initials: HashMap<Version, CryptoState>,
handshake: Option<CryptoState>,
zero_rtt: Option<CryptoDxState>, // One direction only!
cipher: Cipher,
@ -757,14 +798,15 @@ impl CryptoStates {
/// not yet available.
pub fn select_tx_mut(
&mut self,
version: Version,
space: PacketNumberSpace,
) -> Option<(CryptoSpace, &mut CryptoDxState)> {
match space {
PacketNumberSpace::Initial => self
.tx_mut(CryptoSpace::Initial)
.tx_mut(version, CryptoSpace::Initial)
.map(|dx| (CryptoSpace::Initial, dx)),
PacketNumberSpace::Handshake => self
.tx_mut(CryptoSpace::Handshake)
.tx_mut(version, CryptoSpace::Handshake)
.map(|dx| (CryptoSpace::Handshake, dx)),
PacketNumberSpace::ApplicationData => {
if let Some(app) = self.app_write.as_mut() {
@ -776,10 +818,14 @@ impl CryptoStates {
}
}
pub fn tx_mut<'a>(&'a mut self, cspace: CryptoSpace) -> Option<&'a mut CryptoDxState> {
pub fn tx_mut<'a>(
&'a mut self,
version: Version,
cspace: CryptoSpace,
) -> Option<&'a mut CryptoDxState> {
let tx = |k: Option<&'a mut CryptoState>| k.map(|dx| &mut dx.tx);
match cspace {
CryptoSpace::Initial => tx(self.initial.as_mut()),
CryptoSpace::Initial => tx(self.initials.get_mut(&version)),
CryptoSpace::ZeroRtt => self
.zero_rtt
.as_mut()
@ -789,10 +835,10 @@ impl CryptoStates {
}
}
pub fn tx<'a>(&'a self, cspace: CryptoSpace) -> Option<&'a CryptoDxState> {
pub fn tx<'a>(&'a self, version: Version, cspace: CryptoSpace) -> Option<&'a CryptoDxState> {
let tx = |k: Option<&'a CryptoState>| k.map(|dx| &dx.tx);
match cspace {
CryptoSpace::Initial => tx(self.initial.as_ref()),
CryptoSpace::Initial => tx(self.initials.get(&version)),
CryptoSpace::ZeroRtt => self
.zero_rtt
.as_ref()
@ -802,13 +848,17 @@ impl CryptoStates {
}
}
pub fn select_tx(&self, space: PacketNumberSpace) -> Option<(CryptoSpace, &CryptoDxState)> {
pub fn select_tx(
&self,
version: Version,
space: PacketNumberSpace,
) -> Option<(CryptoSpace, &CryptoDxState)> {
match space {
PacketNumberSpace::Initial => self
.tx(CryptoSpace::Initial)
.tx(version, CryptoSpace::Initial)
.map(|dx| (CryptoSpace::Initial, dx)),
PacketNumberSpace::Handshake => self
.tx(CryptoSpace::Handshake)
.tx(version, CryptoSpace::Handshake)
.map(|dx| (CryptoSpace::Handshake, dx)),
PacketNumberSpace::ApplicationData => {
if let Some(app) = self.app_write.as_ref() {
@ -820,22 +870,23 @@ impl CryptoStates {
}
}
pub fn rx_hp(&mut self, cspace: CryptoSpace) -> Option<&mut CryptoDxState> {
pub fn rx_hp(&mut self, version: Version, cspace: CryptoSpace) -> Option<&mut CryptoDxState> {
if let CryptoSpace::ApplicationData = cspace {
self.app_read.as_mut().map(|ar| &mut ar.dx)
} else {
self.rx(cspace, false)
self.rx(version, cspace, false)
}
}
pub fn rx<'a>(
&'a mut self,
version: Version,
cspace: CryptoSpace,
key_phase: bool,
) -> Option<&'a mut CryptoDxState> {
let rx = |x: Option<&'a mut CryptoState>| x.map(|dx| &mut dx.rx);
match cspace {
CryptoSpace::Initial => rx(self.initial.as_mut()),
CryptoSpace::Initial => rx(self.initials.get_mut(&version)),
CryptoSpace::ZeroRtt => self
.zero_rtt
.as_mut()
@ -864,52 +915,101 @@ impl CryptoStates {
pub fn rx_pending(&self, space: CryptoSpace) -> bool {
match space {
CryptoSpace::Initial | CryptoSpace::ZeroRtt => false,
CryptoSpace::Handshake => self.handshake.is_none() && self.initial.is_some(),
CryptoSpace::Handshake => self.handshake.is_none() && !self.initials.is_empty(),
CryptoSpace::ApplicationData => self.app_read.is_none(),
}
}
/// Create the initial crypto state.
pub fn init(&mut self, quic_version: QuicVersion, role: Role, dcid: &[u8]) {
/// Note that the version here can change and that's OK.
pub fn init<'v, V>(&mut self, versions: V, role: Role, dcid: &[u8])
where
V: IntoIterator<Item = &'v Version>,
{
const CLIENT_INITIAL_LABEL: &str = "client in";
const SERVER_INITIAL_LABEL: &str = "server in";
qinfo!(
[self],
"Creating initial cipher state role={:?} dcid={}",
role,
hex(dcid)
);
let (write, read) = match role {
Role::Client => (CLIENT_INITIAL_LABEL, SERVER_INITIAL_LABEL),
Role::Server => (SERVER_INITIAL_LABEL, CLIENT_INITIAL_LABEL),
};
let mut initial = CryptoState {
tx: CryptoDxState::new_initial(quic_version, CryptoDxDirection::Write, write, dcid),
rx: CryptoDxState::new_initial(quic_version, CryptoDxDirection::Read, read, dcid),
};
if let Some(prev) = &self.initial {
for v in versions {
qinfo!(
[self],
"Continue packet numbers for initial after retry (write is {:?})",
prev.rx.used_pn,
"Creating initial cipher state v={:?}, role={:?} dcid={}",
v,
role,
hex(dcid)
);
initial.tx.continuation(&prev.tx).unwrap();
let mut initial = CryptoState {
tx: CryptoDxState::new_initial(*v, CryptoDxDirection::Write, write, dcid),
rx: CryptoDxState::new_initial(*v, CryptoDxDirection::Read, read, dcid),
};
if let Some(prev) = self.initials.get(v) {
qinfo!(
[self],
"Continue packet numbers for initial after retry (write is {:?})",
prev.rx.used_pn,
);
initial.tx.continuation(&prev.tx).unwrap();
}
self.initials.insert(*v, initial);
}
self.initial = Some(initial);
}
pub fn set_0rtt_keys(&mut self, dir: CryptoDxDirection, secret: &SymKey, cipher: Cipher) {
/// At a server, we can be more targeted in initializing.
/// Initialize on demand: either to decrypt Initial packets that we receive
/// or after a version has been selected.
/// This is maybe slightly inefficient in the first case, because we might
/// not need the send keys if the packet is subsequently discarded, but
/// the overall effort is small enough to write off.
pub fn init_server(&mut self, version: Version, dcid: &[u8]) {
if !self.initials.contains_key(&version) {
self.init(&[version], Role::Server, dcid);
}
}
pub fn confirm_version(&mut self, orig: Version, confirmed: Version) {
if orig != confirmed {
// This part where the old data is removed and then re-added is to
// appease the borrow checker.
// Note that on the server, we might not have initials for |orig| if it
// was configured for |orig| and only |confirmed| Initial packets arrived.
if let Some(prev) = self.initials.remove(&orig) {
let next = self.initials.get_mut(&confirmed).unwrap();
next.tx.continuation(&prev.tx).unwrap();
self.initials.insert(orig, prev);
}
}
}
pub fn set_0rtt_keys(
&mut self,
version: Version,
dir: CryptoDxDirection,
secret: &SymKey,
cipher: Cipher,
) {
qtrace!([self], "install 0-RTT keys");
self.zero_rtt = Some(CryptoDxState::new(dir, TLS_EPOCH_ZERO_RTT, secret, cipher));
self.zero_rtt = Some(CryptoDxState::new(
version,
dir,
TLS_EPOCH_ZERO_RTT,
secret,
cipher,
));
}
/// Discard keys and return true if that happened.
pub fn discard(&mut self, space: PacketNumberSpace) -> bool {
match space {
PacketNumberSpace::Initial => self.initial.take().is_some(),
PacketNumberSpace::Initial => {
let empty = self.initials.is_empty();
self.initials.clear();
!empty
}
PacketNumberSpace::Handshake => self.handshake.take().is_some(),
PacketNumberSpace::ApplicationData => panic!("Can't drop application data keys"),
}
@ -926,6 +1026,7 @@ impl CryptoStates {
pub fn set_handshake_keys(
&mut self,
version: Version,
write_secret: &SymKey,
read_secret: &SymKey,
cipher: Cipher,
@ -933,12 +1034,14 @@ impl CryptoStates {
self.cipher = cipher;
self.handshake = Some(CryptoState {
tx: CryptoDxState::new(
version,
CryptoDxDirection::Write,
TLS_EPOCH_HANDSHAKE,
write_secret,
cipher,
),
rx: CryptoDxState::new(
version,
CryptoDxDirection::Read,
TLS_EPOCH_HANDSHAKE,
read_secret,
@ -947,10 +1050,10 @@ impl CryptoStates {
});
}
pub fn set_application_write_key(&mut self, secret: SymKey) -> Res<()> {
pub fn set_application_write_key(&mut self, version: Version, secret: SymKey) -> Res<()> {
debug_assert!(self.app_write.is_none());
debug_assert_ne!(self.cipher, 0);
let mut app = CryptoDxAppData::new(CryptoDxDirection::Write, secret, self.cipher)?;
let mut app = CryptoDxAppData::new(version, CryptoDxDirection::Write, secret, self.cipher)?;
if let Some(z) = &self.zero_rtt {
if z.direction == CryptoDxDirection::Write {
app.dx.continuation(z)?;
@ -961,10 +1064,15 @@ impl CryptoStates {
Ok(())
}
pub fn set_application_read_key(&mut self, secret: SymKey, expire_0rtt: Instant) -> Res<()> {
pub fn set_application_read_key(
&mut self,
version: Version,
secret: SymKey,
expire_0rtt: Instant,
) -> Res<()> {
debug_assert!(self.app_write.is_some(), "should have write keys installed");
debug_assert!(self.app_read.is_none());
let mut app = CryptoDxAppData::new(CryptoDxDirection::Read, secret, self.cipher)?;
let mut app = CryptoDxAppData::new(version, CryptoDxDirection::Read, secret, self.cipher)?;
if let Some(z) = &self.zero_rtt {
if z.direction == CryptoDxDirection::Read {
app.dx.continuation(z)?;
@ -1120,11 +1228,16 @@ impl CryptoStates {
cipher: TLS_AES_128_GCM_SHA256,
next_secret: hkdf::import_key(TLS_VERSION_1_3, &[0xaa; 32]).unwrap(),
};
Self {
initial: Some(CryptoState {
let mut initials = HashMap::new();
initials.insert(
Version::Version1,
CryptoState {
tx: CryptoDxState::test_default(),
rx: read(0),
}),
},
);
Self {
initials,
handshake: None,
zero_rtt: None,
cipher: TLS_AES_128_GCM_SHA256,
@ -1146,13 +1259,14 @@ impl CryptoStates {
let secret = hkdf::import_key(TLS_VERSION_1_3, SECRET).unwrap();
let app_read = |epoch| CryptoDxAppData {
dx: CryptoDxState {
version: Version::Version1,
direction: CryptoDxDirection::Read,
epoch,
aead: Aead::new(
TLS_VERSION_1_3,
TLS_CHACHA20_POLY1305_SHA256,
&secret,
"quic ",
"quic ", // This is a v1 test so hard-code the label.
)
.unwrap(),
hpkey: HpKey::extract(
@ -1170,7 +1284,7 @@ impl CryptoStates {
next_secret: secret.clone(),
};
Self {
initial: None,
initials: HashMap::new(),
handshake: None,
zero_rtt: None,
cipher: TLS_CHACHA20_POLY1305_SHA256,

4
third_party/rust/neqo-transport/src/dump.rs поставляемый
Просмотреть файл

@ -13,6 +13,8 @@ use crate::packet::{PacketNumber, PacketType};
use crate::path::PathRef;
use neqo_common::{qdebug, Decoder};
use std::fmt::Write;
#[allow(clippy::module_name_repetitions)]
pub fn dump_packet(
conn: &Connection,
@ -37,7 +39,7 @@ pub fn dump_packet(
}
};
if let Some(x) = f.dump() {
s.push_str(&format!("\n {} {}", dir, &x));
write!(&mut s, "\n {} {}", dir, &x).unwrap();
}
}
qdebug!([conn], "pn={} type={:?} {}{}", pn, pt, path.borrow(), s);

Просмотреть файл

@ -93,13 +93,13 @@ impl From<ConnectionError> for CloseError {
}
}
#[derive(PartialEq, Debug, Default, Clone)]
#[derive(PartialEq, Eq, Debug, Default, Clone)]
pub struct AckRange {
pub(crate) gap: u64,
pub(crate) range: u64,
}
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Frame<'a> {
Padding,
Ping,

7
third_party/rust/neqo-transport/src/lib.rs поставляемый
Просмотреть файл

@ -36,6 +36,7 @@ pub mod stream_id;
pub mod streams;
pub mod tparams;
mod tracking;
pub mod version;
pub use self::cc::CongestionControlAlgorithm;
pub use self::cid::{
@ -47,9 +48,9 @@ pub use self::connection::{
};
pub use self::events::{ConnectionEvent, ConnectionEvents};
pub use self::frame::CloseError;
pub use self::packet::QuicVersion;
pub use self::stats::Stats;
pub use self::stream_id::{StreamId, StreamType};
pub use self::version::Version;
pub use self::recv_stream::RECV_BUFFER_SIZE;
pub use self::send_stream::SEND_BUFFER_SIZE;
@ -86,6 +87,7 @@ pub enum Error {
ConnectionState,
DecodingFrame,
DecryptError,
DisabledVersion,
HandshakeFailed,
IdleTimeout,
IntegerOverflow,
@ -95,7 +97,7 @@ pub enum Error {
InvalidResumptionToken,
InvalidRetry,
InvalidStreamId,
KeysDiscarded,
KeysDiscarded(crypto::CryptoSpace),
/// Packet protection keys are exhausted.
/// Also used when too many key updates have happened.
KeysExhausted,
@ -143,6 +145,7 @@ impl Error {
// As we have a special error code for ECH fallbacks, we lose the alert.
// Send the server "ech_required" directly.
Self::EchRetry(_) => 0x100 + 121,
Self::VersionNegotiation => 0x53f8,
// All the rest are internal errors.
_ => 1,
}

Просмотреть файл

@ -7,6 +7,7 @@
// Encoding and decoding packets off the wire.
use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdRef, MAX_CONNECTION_ID_LEN};
use crate::crypto::{CryptoDxState, CryptoSpace, CryptoStates};
use crate::version::{Version, WireVersion};
use crate::{Error, Res};
use neqo_common::{hex, hex_with_len, qtrace, qwarn, Decoder, Encoder};
@ -19,11 +20,6 @@ use std::iter::ExactSizeIterator;
use std::ops::{Deref, DerefMut, Range};
use std::time::Instant;
const PACKET_TYPE_INITIAL: u8 = 0x0;
const PACKET_TYPE_0RTT: u8 = 0x01;
const PACKET_TYPE_HANDSHAKE: u8 = 0x2;
const PACKET_TYPE_RETRY: u8 = 0x03;
pub const PACKET_BIT_LONG: u8 = 0x80;
const PACKET_BIT_SHORT: u8 = 0x00;
const PACKET_BIT_FIXED_QUIC: u8 = 0x40;
@ -40,7 +36,6 @@ const MAX_PACKET_NUMBER_LEN: usize = 4;
mod retry;
pub type PacketNumber = u64;
type Version = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PacketType {
@ -55,15 +50,29 @@ pub enum PacketType {
impl PacketType {
#[must_use]
fn code(self) -> u8 {
match self {
Self::Initial => PACKET_TYPE_INITIAL,
Self::ZeroRtt => PACKET_TYPE_0RTT,
Self::Handshake => PACKET_TYPE_HANDSHAKE,
Self::Retry => PACKET_TYPE_RETRY,
_ => panic!("shouldn't be here"),
fn from_byte(t: u8, v: Version) -> Self {
// Version2 adds one to the type, modulo 4
match t.wrapping_sub(u8::from(v == Version::Version2)) & 3 {
0 => Self::Initial,
1 => Self::ZeroRtt,
2 => Self::Handshake,
3 => Self::Retry,
_ => panic!("packet type out of range"),
}
}
#[must_use]
fn to_byte(self, v: Version) -> u8 {
let t = match self {
Self::Initial => 0,
Self::ZeroRtt => 1,
Self::Handshake => 2,
Self::Retry => 3,
_ => panic!("not a long header packet type"),
};
// Version2 adds one to the type, modulo 4
(t + u8::from(v == Version::Version2)) & 3
}
}
impl From<PacketType> for CryptoSpace {
@ -89,53 +98,6 @@ impl From<CryptoSpace> for PacketType {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum QuicVersion {
Version1,
Draft29,
Draft30,
Draft31,
Draft32,
}
impl QuicVersion {
pub fn as_u32(self) -> Version {
match self {
Self::Version1 => 1,
Self::Draft29 => 0xff00_0000 + 29,
Self::Draft30 => 0xff00_0000 + 30,
Self::Draft31 => 0xff00_0000 + 31,
Self::Draft32 => 0xff00_0000 + 32,
}
}
}
impl Default for QuicVersion {
fn default() -> Self {
Self::Version1
}
}
impl TryFrom<Version> for QuicVersion {
type Error = Error;
fn try_from(ver: Version) -> Res<Self> {
if ver == 1 {
Ok(Self::Version1)
} else if ver == 0xff00_0000 + 29 {
Ok(Self::Draft29)
} else if ver == 0xff00_0000 + 30 {
Ok(Self::Draft30)
} else if ver == 0xff00_0000 + 31 {
Ok(Self::Draft31)
} else if ver == 0xff00_0000 + 32 {
Ok(Self::Draft32)
} else {
Err(Error::VersionNegotiation)
}
}
}
struct PacketBuilderOffsets {
/// The bits of the first octet that need masking.
first_byte_mask: u8,
@ -214,7 +176,7 @@ impl PacketBuilder {
pub fn long(
mut encoder: Encoder,
pt: PacketType,
quic_version: QuicVersion,
version: Version,
dcid: impl AsRef<[u8]>,
scid: impl AsRef<[u8]>,
) -> Self {
@ -225,8 +187,8 @@ impl PacketBuilder {
if limit > encoder.len()
&& 11 + dcid.as_ref().len() + scid.as_ref().len() < limit - encoder.len()
{
encoder.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC | pt.code() << 4);
encoder.encode_uint(4, quic_version.as_u32());
encoder.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC | pt.to_byte(version) << 4);
encoder.encode_uint(4, version.wire_version());
encoder.encode_vec(1, dcid.as_ref());
encoder.encode_vec(1, scid.as_ref());
} else {
@ -248,7 +210,7 @@ impl PacketBuilder {
}
fn is_long(&self) -> bool {
self[self.header.start] & 0x80 == PACKET_BIT_LONG
self.as_ref()[self.header.start] & 0x80 == PACKET_BIT_LONG
}
/// This stores a value that can be used as a limit. This does not cause
@ -305,16 +267,12 @@ impl PacketBuilder {
let mask = if quic_bit { PACKET_BIT_FIXED_QUIC } else { 0 }
| if self.is_long() { 0 } else { PACKET_BIT_SPIN };
let first = self.header.start;
self[first] ^= random(1)[0] & mask;
self.encoder.as_mut()[first] ^= random(1)[0] & mask;
}
/// For an Initial packet, encode the token.
/// If you fail to do this, then you will not get a valid packet.
pub fn initial_token(&mut self, token: &[u8]) {
debug_assert_eq!(
self.encoder[self.header.start] & 0xb0,
PACKET_BIT_LONG | PACKET_TYPE_INITIAL << 4
);
if Encoder::vvec_len(token.len()) < self.remaining() {
self.encoder.encode_vvec(token);
} else {
@ -348,15 +306,15 @@ impl PacketBuilder {
self.offsets.pn = pn_offset..self.encoder.len();
// Now encode the packet number length and save the header length.
self.encoder[self.header.start] |= u8::try_from(pn_len - 1).unwrap();
self.encoder.as_mut()[self.header.start] |= u8::try_from(pn_len - 1).unwrap();
self.header.end = self.encoder.len();
self.pn = pn;
}
fn write_len(&mut self, expansion: usize) {
let len = self.encoder.len() - (self.offsets.len + 2) + expansion;
self.encoder[self.offsets.len] = 0x40 | ((len >> 8) & 0x3f) as u8;
self.encoder[self.offsets.len + 1] = (len & 0xff) as u8;
self.encoder.as_mut()[self.offsets.len] = 0x40 | ((len >> 8) & 0x3f) as u8;
self.encoder.as_mut()[self.offsets.len + 1] = (len & 0xff) as u8;
}
fn pad_for_crypto(&mut self, crypto: &mut CryptoDxState) {
@ -402,8 +360,8 @@ impl PacketBuilder {
self.write_len(crypto.expansion());
}
let hdr = &self.encoder[self.header.clone()];
let body = &self.encoder[self.header.end..];
let hdr = &self.encoder.as_ref()[self.header.clone()];
let body = &self.encoder.as_ref()[self.header.end..];
qtrace!(
"Packet build pn={} hdr={} body={}",
self.pn,
@ -419,9 +377,9 @@ impl PacketBuilder {
let mask = crypto.compute_mask(sample)?;
// Apply the mask.
self.encoder[self.header.start] ^= mask[0] & self.offsets.first_byte_mask;
self.encoder.as_mut()[self.header.start] ^= mask[0] & self.offsets.first_byte_mask;
for (i, j) in (1..=self.offsets.pn.len()).zip(self.offsets.pn) {
self.encoder[j] ^= mask[i];
self.encoder.as_mut()[j] ^= mask[i];
}
// Finally, cut off the plaintext and add back the ciphertext.
@ -449,7 +407,7 @@ impl PacketBuilder {
/// As Retry is odd (it has to be constructed with leading bytes),
/// this returns a Vec<u8> rather than building on an encoder.
pub fn retry(
quic_version: QuicVersion,
version: Version,
dcid: &[u8],
scid: &[u8],
token: &[u8],
@ -461,17 +419,17 @@ impl PacketBuilder {
encoder.encode_byte(
PACKET_BIT_LONG
| PACKET_BIT_FIXED_QUIC
| (PACKET_TYPE_RETRY << 4)
| (PacketType::Retry.to_byte(version) << 4)
| (random(1)[0] & 0xf),
);
encoder.encode_uint(4, quic_version.as_u32());
encoder.encode_uint(4, version.wire_version());
encoder.encode_vec(1, dcid);
encoder.encode_vec(1, scid);
debug_assert_ne!(token.len(), 0);
encoder.encode(token);
let tag = retry::use_aead(quic_version, |aead| {
let tag = retry::use_aead(version, |aead| {
let mut buf = vec![0; aead.expansion()];
Ok(aead.encrypt(0, &encoder, &[], &mut buf)?.to_vec())
Ok(aead.encrypt(0, encoder.as_ref(), &[], &mut buf)?.to_vec())
})?;
encoder.encode(&tag);
let mut complete: Vec<u8> = encoder.into();
@ -479,25 +437,34 @@ impl PacketBuilder {
}
/// Make a Version Negotiation packet.
pub fn version_negotiation(dcid: &[u8], scid: &[u8]) -> Vec<u8> {
pub fn version_negotiation(
dcid: &[u8],
scid: &[u8],
client_version: u32,
versions: &[Version],
) -> Vec<u8> {
let mut encoder = Encoder::default();
let mut grease = random(5);
let mut grease = random(4);
// This will not include the "QUIC bit" sometimes. Intentionally.
encoder.encode_byte(PACKET_BIT_LONG | (grease[4] & 0x7f));
encoder.encode_byte(PACKET_BIT_LONG | (grease[3] & 0x7f));
encoder.encode(&[0; 4]); // Zero version == VN.
encoder.encode_vec(1, dcid);
encoder.encode_vec(1, scid);
encoder.encode_uint(4, QuicVersion::Version1.as_u32());
encoder.encode_uint(4, QuicVersion::Draft29.as_u32());
encoder.encode_uint(4, QuicVersion::Draft30.as_u32());
encoder.encode_uint(4, QuicVersion::Draft31.as_u32());
encoder.encode_uint(4, QuicVersion::Draft32.as_u32());
for v in versions {
encoder.encode_uint(4, v.wire_version());
}
// Add a greased version, using the randomness already generated.
for g in &mut grease[..4] {
for g in &mut grease[..3] {
*g = *g & 0xf0 | 0x0a;
}
encoder.encode(&grease[0..4]);
encoder.into()
// Ensure our greased version does not collide with the client version
// by making the last byte differ from the client initial.
grease[3] = (client_version.wrapping_add(0x10) & 0xf0) as u8 | 0x0a;
encoder.encode(&grease[..4]);
Vec::from(encoder)
}
}
@ -536,7 +503,7 @@ pub struct PublicPacket<'a> {
/// The size of the header, not including the packet number.
header_len: usize,
/// Protocol version, if present in header.
quic_version: Option<QuicVersion>,
version: Option<WireVersion>,
/// A reference to the entire packet, including the header.
data: &'a [u8],
}
@ -556,11 +523,11 @@ impl<'a> PublicPacket<'a> {
fn decode_long(
decoder: &mut Decoder<'a>,
packet_type: PacketType,
quic_version: QuicVersion,
version: Version,
) -> Res<(&'a [u8], usize)> {
if packet_type == PacketType::Retry {
let header_len = decoder.offset();
let expansion = retry::expansion(quic_version);
let expansion = retry::expansion(version);
let token = Self::opt(decoder.decode(decoder.remaining() - expansion))?;
if token.is_empty() {
return Err(Error::InvalidPacket);
@ -603,7 +570,7 @@ impl<'a> PublicPacket<'a> {
scid: None,
token: &[],
header_len,
quic_version: None,
version: None,
data,
},
&[],
@ -611,7 +578,7 @@ impl<'a> PublicPacket<'a> {
}
// Generic long header.
let version = Version::try_from(Self::opt(decoder.decode_uint(4))?).unwrap();
let version = WireVersion::try_from(Self::opt(decoder.decode_uint(4))?).unwrap();
let dcid = ConnectionIdRef::from(Self::opt(decoder.decode_vec(1))?);
let scid = ConnectionIdRef::from(Self::opt(decoder.decode_vec(1))?);
@ -624,7 +591,7 @@ impl<'a> PublicPacket<'a> {
scid: Some(scid),
token: &[],
header_len: decoder.offset(),
quic_version: None,
version: None,
data,
},
&[],
@ -632,7 +599,7 @@ impl<'a> PublicPacket<'a> {
}
// Check that this is a long header from a supported version.
let quic_version = if let Ok(v) = QuicVersion::try_from(version) {
let version = if let Ok(v) = Version::try_from(version) {
v
} else {
return Ok((
@ -642,7 +609,7 @@ impl<'a> PublicPacket<'a> {
scid: Some(scid),
token: &[],
header_len: decoder.offset(),
quic_version: None,
version: Some(version),
data,
},
&[],
@ -652,16 +619,10 @@ impl<'a> PublicPacket<'a> {
if dcid.len() > MAX_CONNECTION_ID_LEN || scid.len() > MAX_CONNECTION_ID_LEN {
return Err(Error::InvalidPacket);
}
let packet_type = match (first >> 4) & 3 {
PACKET_TYPE_INITIAL => PacketType::Initial,
PACKET_TYPE_0RTT => PacketType::ZeroRtt,
PACKET_TYPE_HANDSHAKE => PacketType::Handshake,
PACKET_TYPE_RETRY => PacketType::Retry,
_ => unreachable!(),
};
let packet_type = PacketType::from_byte((first >> 4) & 3, version);
// The type-specific code includes a token. This consumes the remainder of the packet.
let (token, header_len) = Self::decode_long(&mut decoder, packet_type, quic_version)?;
let (token, header_len) = Self::decode_long(&mut decoder, packet_type, version)?;
let end = data.len() - decoder.remaining();
let (data, remainder) = data.split_at(end);
Ok((
@ -671,7 +632,7 @@ impl<'a> PublicPacket<'a> {
scid: Some(scid),
token,
header_len,
quic_version: Some(quic_version),
version: Some(version.wire_version()),
data,
},
remainder,
@ -683,7 +644,7 @@ impl<'a> PublicPacket<'a> {
if self.packet_type != PacketType::Retry {
return false;
}
let version = self.quic_version.unwrap();
let version = self.version().unwrap();
let expansion = retry::expansion(version);
if self.data.len() <= expansion {
return false;
@ -694,7 +655,7 @@ impl<'a> PublicPacket<'a> {
encoder.encode(header);
retry::use_aead(version, |aead| {
let mut buf = vec![0; expansion];
Ok(aead.decrypt(0, &encoder, tag, &mut buf)?.is_empty())
Ok(aead.decrypt(0, encoder.as_ref(), tag, &mut buf)?.is_empty())
})
.unwrap_or(false)
}
@ -724,8 +685,13 @@ impl<'a> PublicPacket<'a> {
self.token
}
pub fn version(&self) -> Option<QuicVersion> {
self.quic_version
pub fn version(&self) -> Option<Version> {
self.version.and_then(|v| Version::try_from(v).ok())
}
pub fn wire_version(&self) -> WireVersion {
debug_assert!(self.version.is_some());
self.version.unwrap_or(0)
}
pub fn len(&self) -> usize {
@ -808,16 +774,20 @@ impl<'a> PublicPacket<'a> {
pub fn decrypt(&self, crypto: &mut CryptoStates, release_at: Instant) -> Res<DecryptedPacket> {
let cspace: CryptoSpace = self.packet_type.into();
// When we don't have a version, the crypto code doesn't need a version
// for lookup, so use the default, but fix it up if decryption succeeds.
let version = self.version().unwrap_or_default();
// This has to work in two stages because we need to remove header protection
// before picking the keys to use.
if let Some(rx) = crypto.rx_hp(cspace) {
if let Some(rx) = crypto.rx_hp(version, cspace) {
// Note that this will dump early, which creates a side-channel.
// This is OK in this case because we the only reason this can
// fail is if the cryptographic module is bad or the packet is
// too small (which is public information).
let (key_phase, pn, header, body) = self.decrypt_header(rx)?;
qtrace!([rx], "decoded header: {:?}", header);
let rx = crypto.rx(cspace, key_phase).unwrap();
let rx = crypto.rx(version, cspace, key_phase).unwrap();
let version = rx.version(); // Version fixup; see above.
let d = rx.decrypt(pn, &header, body)?;
// If this is the first packet ever successfully decrypted
// using `rx`, make sure to initiate a key update.
@ -826,6 +796,7 @@ impl<'a> PublicPacket<'a> {
}
crypto.check_pn_overlap()?;
Ok(DecryptedPacket {
version,
pt: self.packet_type,
pn,
data: d,
@ -834,16 +805,16 @@ impl<'a> PublicPacket<'a> {
Err(Error::KeysPending(cspace))
} else {
qtrace!("keys for {:?} already discarded", cspace);
Err(Error::KeysDiscarded)
Err(Error::KeysDiscarded(cspace))
}
}
pub fn supported_versions(&self) -> Res<Vec<Version>> {
pub fn supported_versions(&self) -> Res<Vec<WireVersion>> {
assert_eq!(self.packet_type, PacketType::VersionNegotiation);
let mut decoder = Decoder::new(&self.data[self.header_len..]);
let mut res = Vec::new();
while decoder.remaining() > 0 {
let version = Version::try_from(Self::opt(decoder.decode_uint(4))?)?;
let version = WireVersion::try_from(Self::opt(decoder.decode_uint(4))?)?;
res.push(version);
}
Ok(res)
@ -863,12 +834,17 @@ impl fmt::Debug for PublicPacket<'_> {
}
pub struct DecryptedPacket {
version: Version,
pt: PacketType,
pn: PacketNumber,
data: Vec<u8>,
}
impl DecryptedPacket {
pub fn version(&self) -> Version {
self.version
}
pub fn packet_type(&self) -> PacketType {
self.pt
}
@ -890,7 +866,7 @@ impl Deref for DecryptedPacket {
mod tests {
use super::*;
use crate::crypto::{CryptoDxState, CryptoStates};
use crate::{EmptyConnectionIdGenerator, QuicVersion, RandomConnectionIdGenerator};
use crate::{EmptyConnectionIdGenerator, RandomConnectionIdGenerator, Version};
use neqo_common::Encoder;
use test_fixture::{fixture_init, now};
@ -936,7 +912,7 @@ mod tests {
let mut builder = PacketBuilder::long(
Encoder::new(),
PacketType::Initial,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(&[][..]),
&ConnectionId::from(SERVER_CID),
);
@ -944,7 +920,7 @@ mod tests {
builder.pn(1, 2);
builder.encode(SAMPLE_INITIAL_PAYLOAD);
let packet = builder.build(&mut prot).expect("build");
assert_eq!(&packet[..], SAMPLE_INITIAL);
assert_eq!(packet.as_ref(), SAMPLE_INITIAL);
}
#[test]
@ -971,24 +947,24 @@ mod tests {
fn disallow_long_dcid() {
let mut enc = Encoder::new();
enc.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC);
enc.encode_uint(4, QuicVersion::default().as_u32());
enc.encode_uint(4, Version::default().wire_version());
enc.encode_vec(1, &[0x00; MAX_CONNECTION_ID_LEN + 1]);
enc.encode_vec(1, &[]);
enc.encode(&[0xff; 40]); // junk
assert!(PublicPacket::decode(&enc, &cid_mgr()).is_err());
assert!(PublicPacket::decode(enc.as_ref(), &cid_mgr()).is_err());
}
#[test]
fn disallow_long_scid() {
let mut enc = Encoder::new();
enc.encode_byte(PACKET_BIT_LONG | PACKET_BIT_FIXED_QUIC);
enc.encode_uint(4, QuicVersion::default().as_u32());
enc.encode_uint(4, Version::default().wire_version());
enc.encode_vec(1, &[]);
enc.encode_vec(1, &[0x00; MAX_CONNECTION_ID_LEN + 2]);
enc.encode(&[0xff; 40]); // junk
assert!(PublicPacket::decode(&enc, &cid_mgr()).is_err());
assert!(PublicPacket::decode(enc.as_ref(), &cid_mgr()).is_err());
}
const SAMPLE_SHORT: &[u8] = &[
@ -1007,7 +983,7 @@ mod tests {
let packet = builder
.build(&mut CryptoDxState::test_default())
.expect("build");
assert_eq!(&packet[..], SAMPLE_SHORT);
assert_eq!(packet.as_ref(), SAMPLE_SHORT);
}
#[test]
@ -1019,7 +995,7 @@ mod tests {
PacketBuilder::short(Encoder::new(), true, &ConnectionId::from(SERVER_CID));
builder.scramble(true);
builder.pn(0, 1);
firsts.push(builder[0]);
firsts.push(builder.as_ref()[0]);
}
let is_set = |bit| move |v| v & bit == bit;
// There should be at least one value with the QUIC bit set:
@ -1077,7 +1053,7 @@ mod tests {
let mut builder = PacketBuilder::long(
Encoder::new(),
PacketType::Handshake,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(SERVER_CID),
&ConnectionId::from(CLIENT_CID),
);
@ -1092,8 +1068,8 @@ mod tests {
builder.encode(&[0]); // Minimal size (packet number is big enough).
let encoder = builder.build(&mut prot).expect("build");
assert_eq!(
&first[..],
&encoder[..first.len()],
first.as_ref(),
&encoder.as_ref()[..first.len()],
"the first packet should be a prefix"
);
assert_eq!(encoder.len(), 45 + 29);
@ -1111,14 +1087,14 @@ mod tests {
let mut builder = PacketBuilder::long(
Encoder::new(),
PacketType::Handshake,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(&[][..]),
&ConnectionId::from(&[][..]),
);
builder.pn(0, 1);
builder.encode(&[1, 2, 3]);
let packet = builder.build(&mut CryptoDxState::test_default()).unwrap();
assert_eq!(&packet[..], EXPECTED);
assert_eq!(packet.as_ref(), EXPECTED);
}
#[test]
@ -1130,13 +1106,13 @@ mod tests {
let mut builder = PacketBuilder::long(
Encoder::new(),
PacketType::Handshake,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(&[][..]),
&ConnectionId::from(&[][..]),
);
builder.pn(0, 1);
builder.scramble(true);
if (builder[0] & PACKET_BIT_FIXED_QUIC) == 0 {
if (builder.as_ref()[0] & PACKET_BIT_FIXED_QUIC) == 0 {
found_unset = true;
} else {
found_set = true;
@ -1151,7 +1127,7 @@ mod tests {
let mut builder = PacketBuilder::long(
Encoder::new(),
PacketType::Initial,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(&[][..]),
&ConnectionId::from(SERVER_CID),
);
@ -1185,7 +1161,7 @@ mod tests {
let builder = PacketBuilder::long(
encoder,
PacketType::Initial,
QuicVersion::default(),
Version::default(),
&ConnectionId::from(SERVER_CID),
&ConnectionId::from(SERVER_CID),
);
@ -1193,6 +1169,12 @@ mod tests {
assert_eq!(builder.abort(), encoder_copy);
}
const SAMPLE_RETRY_V2: &[u8] = &[
0xcf, 0x70, 0x9a, 0x50, 0xc4, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1d, 0xc7, 0x11, 0x30, 0xcd, 0x1e, 0xd3, 0x9d, 0x6e, 0xfc,
0xee, 0x5c, 0x85, 0x80, 0x65, 0x01,
];
const SAMPLE_RETRY_V1: &[u8] = &[
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5,
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba, 0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58,
@ -1225,10 +1207,10 @@ mod tests {
const RETRY_TOKEN: &[u8] = b"token";
fn build_retry_single(quic_version: QuicVersion, sample_retry: &[u8]) {
fn build_retry_single(version: Version, sample_retry: &[u8]) {
fixture_init();
let retry =
PacketBuilder::retry(quic_version, &[], SERVER_CID, RETRY_TOKEN, CLIENT_CID).unwrap();
PacketBuilder::retry(version, &[], SERVER_CID, RETRY_TOKEN, CLIENT_CID).unwrap();
let (packet, remainder) = PublicPacket::decode(&retry, &cid_mgr()).unwrap();
assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID)));
@ -1240,35 +1222,43 @@ mod tests {
assert_eq!(&retry, &sample_retry);
} else {
// Otherwise, just check that the header is OK.
assert_eq!(retry[0] & 0xf0, 0xf0);
assert_eq!(
retry[0] & 0xf0,
0xc0 | (PacketType::Retry.to_byte(version) << 4)
);
let header_range = 1..retry.len() - 16;
assert_eq!(&retry[header_range.clone()], &sample_retry[header_range]);
}
}
#[test]
fn build_retry_v2() {
build_retry_single(Version::Version2, SAMPLE_RETRY_V2);
}
#[test]
fn build_retry_v1() {
build_retry_single(QuicVersion::Version1, SAMPLE_RETRY_V1);
build_retry_single(Version::Version1, SAMPLE_RETRY_V1);
}
#[test]
fn build_retry_29() {
build_retry_single(QuicVersion::Draft29, SAMPLE_RETRY_29);
build_retry_single(Version::Draft29, SAMPLE_RETRY_29);
}
#[test]
fn build_retry_30() {
build_retry_single(QuicVersion::Draft30, SAMPLE_RETRY_30);
build_retry_single(Version::Draft30, SAMPLE_RETRY_30);
}
#[test]
fn build_retry_31() {
build_retry_single(QuicVersion::Draft31, SAMPLE_RETRY_31);
build_retry_single(Version::Draft31, SAMPLE_RETRY_31);
}
#[test]
fn build_retry_32() {
build_retry_single(QuicVersion::Draft32, SAMPLE_RETRY_32);
build_retry_single(Version::Draft32, SAMPLE_RETRY_32);
}
#[test]
@ -1277,6 +1267,7 @@ mod tests {
// Odds are approximately 1 in 8 that the full comparison doesn't happen
// for a given version.
for _ in 0..32 {
build_retry_v2();
build_retry_v1();
build_retry_29();
build_retry_30();
@ -1285,36 +1276,46 @@ mod tests {
}
}
fn decode_retry(quic_version: QuicVersion, sample_retry: &[u8]) {
fn decode_retry(version: Version, sample_retry: &[u8]) {
fixture_init();
let (packet, remainder) =
PublicPacket::decode(sample_retry, &RandomConnectionIdGenerator::new(5)).unwrap();
assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID)));
assert_eq!(Some(quic_version), packet.quic_version);
assert_eq!(Some(version), packet.version());
assert!(packet.dcid().is_empty());
assert_eq!(&packet.scid()[..], SERVER_CID);
assert_eq!(packet.token(), RETRY_TOKEN);
assert!(remainder.is_empty());
}
#[test]
fn decode_retry_v2() {
decode_retry(Version::Version2, SAMPLE_RETRY_V2);
}
#[test]
fn decode_retry_v1() {
decode_retry(Version::Version1, SAMPLE_RETRY_V1);
}
#[test]
fn decode_retry_29() {
decode_retry(QuicVersion::Draft29, SAMPLE_RETRY_29);
decode_retry(Version::Draft29, SAMPLE_RETRY_29);
}
#[test]
fn decode_retry_30() {
decode_retry(QuicVersion::Draft30, SAMPLE_RETRY_30);
decode_retry(Version::Draft30, SAMPLE_RETRY_30);
}
#[test]
fn decode_retry_31() {
decode_retry(QuicVersion::Draft31, SAMPLE_RETRY_31);
decode_retry(Version::Draft31, SAMPLE_RETRY_31);
}
#[test]
fn decode_retry_32() {
decode_retry(QuicVersion::Draft32, SAMPLE_RETRY_32);
decode_retry(Version::Draft32, SAMPLE_RETRY_32);
}
/// Check some packets that are clearly not valid Retry packets.
@ -1326,11 +1327,11 @@ mod tests {
assert!(PublicPacket::decode(&[], &cid_mgr).is_err());
let (packet, remainder) = PublicPacket::decode(SAMPLE_RETRY_29, &cid_mgr).unwrap();
let (packet, remainder) = PublicPacket::decode(SAMPLE_RETRY_V1, &cid_mgr).unwrap();
assert!(remainder.is_empty());
assert!(packet.is_valid_retry(&odcid));
let mut damaged_retry = SAMPLE_RETRY_29.to_vec();
let mut damaged_retry = SAMPLE_RETRY_V1.to_vec();
let last = damaged_retry.len() - 1;
damaged_retry[last] ^= 66;
let (packet, remainder) = PublicPacket::decode(&damaged_retry, &cid_mgr).unwrap();
@ -1352,15 +1353,16 @@ mod tests {
const SAMPLE_VN: &[u8] = &[
0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x08,
0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00,
0x1d, 0xff, 0x00, 0x00, 0x1e, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x20, 0x0a, 0x0a,
0x0a, 0x0a,
0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x70, 0x9a, 0x50, 0xc4, 0x00, 0x00, 0x00,
0x01, 0xff, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x1e, 0xff, 0x00,
0x00, 0x1d, 0x0a, 0x0a, 0x0a, 0x0a,
];
#[test]
fn build_vn() {
fixture_init();
let mut vn = PacketBuilder::version_negotiation(SERVER_CID, CLIENT_CID);
let mut vn =
PacketBuilder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a0a0a, &Version::all());
// Erase randomness from greasing...
assert_eq!(vn.len(), SAMPLE_VN.len());
vn[0] &= 0x80;
@ -1370,6 +1372,14 @@ mod tests {
assert_eq!(&vn, &SAMPLE_VN);
}
#[test]
fn vn_do_not_repeat_client_grease() {
fixture_init();
let vn =
PacketBuilder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a0a0a, &Version::all());
assert_ne!(&vn[SAMPLE_VN.len() - 4..], &[0x0a, 0x0a, 0x0a, 0x0a]);
}
#[test]
fn parse_vn() {
let (packet, remainder) =
@ -1390,11 +1400,11 @@ mod tests {
enc.encode_vec(1, BIG_DCID);
enc.encode_vec(1, BIG_SCID);
enc.encode_uint(4, 0x1a2a_3a4a_u64);
enc.encode_uint(4, QuicVersion::default().as_u32());
enc.encode_uint(4, Version::default().wire_version());
enc.encode_uint(4, 0x5a6a_7a8a_u64);
let (packet, remainder) =
PublicPacket::decode(&enc, &EmptyConnectionIdGenerator::default()).unwrap();
PublicPacket::decode(enc.as_ref(), &EmptyConnectionIdGenerator::default()).unwrap();
assert!(remainder.is_empty());
assert_eq!(&packet.dcid[..], BIG_DCID);
assert!(packet.scid.is_some());

Просмотреть файл

@ -6,7 +6,7 @@
#![deny(clippy::pedantic)]
use crate::packet::QuicVersion;
use crate::version::Version;
use crate::{Error, Res};
use neqo_common::qerror;
@ -14,37 +14,33 @@ use neqo_crypto::{hkdf, Aead, TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3};
use std::cell::RefCell;
const RETRY_SECRET_29: &[u8] = &[
0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8, 0x0d, 0xf2,
0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa, 0x2b, 0xaa, 0xff, 0x4c,
];
const RETRY_SECRET_V1: &[u8] = &[
0xd9, 0xc9, 0x94, 0x3e, 0x61, 0x01, 0xfd, 0x20, 0x00, 0x21, 0x50, 0x6b, 0xcc, 0x02, 0x81, 0x4c,
0x73, 0x03, 0x0f, 0x25, 0xc7, 0x9d, 0x71, 0xce, 0x87, 0x6e, 0xca, 0x87, 0x6e, 0x6f, 0xca, 0x8e,
];
/// The AEAD used for Retry is fixed, so use thread local storage.
fn make_aead(secret: &[u8]) -> Aead {
fn make_aead(version: Version) -> Aead {
#[cfg(debug_assertions)]
::neqo_crypto::assert_initialized();
let secret = hkdf::import_key(TLS_VERSION_1_3, secret).unwrap();
Aead::new(TLS_VERSION_1_3, TLS_AES_128_GCM_SHA256, &secret, "quic ").unwrap()
let secret = hkdf::import_key(TLS_VERSION_1_3, version.retry_secret()).unwrap();
Aead::new(
TLS_VERSION_1_3,
TLS_AES_128_GCM_SHA256,
&secret,
version.label_prefix(),
)
.unwrap()
}
thread_local!(static RETRY_AEAD_29: RefCell<Aead> = RefCell::new(make_aead(RETRY_SECRET_29)));
thread_local!(static RETRY_AEAD_V1: RefCell<Aead> = RefCell::new(make_aead(RETRY_SECRET_V1)));
thread_local!(static RETRY_AEAD_29: RefCell<Aead> = RefCell::new(make_aead(Version::Draft29)));
thread_local!(static RETRY_AEAD_V1: RefCell<Aead> = RefCell::new(make_aead(Version::Version1)));
thread_local!(static RETRY_AEAD_V2: RefCell<Aead> = RefCell::new(make_aead(Version::Version2)));
/// Run a function with the appropriate Retry AEAD.
pub fn use_aead<F, T>(quic_version: QuicVersion, f: F) -> Res<T>
pub fn use_aead<F, T>(version: Version, f: F) -> Res<T>
where
F: FnOnce(&Aead) -> Res<T>,
{
match quic_version {
QuicVersion::Version1 => &RETRY_AEAD_V1,
QuicVersion::Draft29
| QuicVersion::Draft30
| QuicVersion::Draft31
| QuicVersion::Draft32 => &RETRY_AEAD_29,
match version {
Version::Version2 => &RETRY_AEAD_V2,
Version::Version1 => &RETRY_AEAD_V1,
Version::Draft29 | Version::Draft30 | Version::Draft31 | Version::Draft32 => &RETRY_AEAD_29,
}
.try_with(|aead| f(&aead.borrow()))
.map_err(|e| {
@ -54,8 +50,8 @@ where
}
/// Determine how large the expansion is for a given key.
pub fn expansion(quic_version: QuicVersion) -> usize {
if let Ok(ex) = use_aead(quic_version, |aead| Ok(aead.expansion())) {
pub fn expansion(version: Version) -> usize {
if let Ok(ex) = use_aead(version, |aead| Ok(aead.expansion())) {
ex
} else {
panic!("Unable to access Retry AEAD")

28
third_party/rust/neqo-transport/src/path.rs поставляемый
Просмотреть файл

@ -539,6 +539,9 @@ pub struct Path {
received_bytes: usize,
/// The number of bytes sent on this path.
sent_bytes: usize,
/// For logging of events.
qlog: NeqoQlog,
}
impl Path {
@ -552,7 +555,7 @@ impl Path {
now: Instant,
) -> Self {
let mut sender = PacketSender::new(cc, Self::mtu_by_addr(remote.ip()), now);
sender.set_qlog(qlog);
sender.set_qlog(qlog.clone());
Self {
local,
remote,
@ -566,6 +569,7 @@ impl Path {
sender,
received_bytes: 0,
sent_bytes: 0,
qlog,
}
}
@ -928,7 +932,27 @@ impl Path {
}
/// Discard a packet that previously might have been in-flight.
pub fn discard_packet(&mut self, sent: &SentPacket) {
pub fn discard_packet(&mut self, sent: &SentPacket, now: Instant) {
if self.rtt.first_sample_time().is_none() {
// When discarding a packet there might not be a good RTT estimate.
// But discards only occur after receiving something, so that means
// that there is some RTT information, which is better than nothing.
// Two cases: 1. at the client when handling a Retry and
// 2. at the server when disposing the Initial packet number space.
qinfo!(
[self],
"discarding a packet without an RTT estimate; guessing RTT={:?}",
now - sent.time_sent
);
self.rtt.update(
&mut self.qlog,
now - sent.time_sent,
Duration::new(0, 0),
false,
now,
);
}
self.sender.discard(sent);
}

6
third_party/rust/neqo-transport/src/qlog.rs поставляемый
Просмотреть файл

@ -22,7 +22,7 @@ use crate::path::PathRef;
use crate::stream_id::StreamType as NeqoStreamType;
use crate::tparams::{self, TransportParametersHandler};
use crate::tracking::SentPacket;
use crate::QuicVersion;
use crate::Version;
pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHandler) {
qlog.add_event(|| {
@ -98,7 +98,7 @@ fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) {
Some("QUIC".into()),
p.local_address().port().into(),
p.remote_address().port().into(),
Some(format!("{:x}", QuicVersion::default().as_u32())),
Some(format!("{:x}", Version::default().wire_version())),
Some(format!("{}", p.local_cid())),
Some(format!("{}", p.remote_cid())),
))
@ -110,7 +110,7 @@ pub fn connection_state_updated(qlog: &mut NeqoQlog, new: &State) {
Some(Event::connection_state_updated_min(match new {
State::Init => qlog::ConnectionState::Attempted,
State::WaitInitial => qlog::ConnectionState::Attempted,
State::Handshaking => qlog::ConnectionState::Handshake,
State::WaitVersion | State::Handshaking => qlog::ConnectionState::Handshake,
State::Connected => qlog::ConnectionState::Active,
State::Confirmed => qlog::ConnectionState::Active,
State::Closing { .. } => qlog::ConnectionState::Draining,

Просмотреть файл

@ -14,7 +14,6 @@ use neqo_common::Encoder;
use std::cmp::min;
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::ops::Deref;
pub const MAX_QUIC_DATAGRAM: u64 = 65535;
@ -53,10 +52,9 @@ impl QuicDatagram {
}
}
impl Deref for QuicDatagram {
type Target = [u8];
impl AsRef<[u8]> for QuicDatagram {
#[must_use]
fn deref(&self) -> &[u8] {
fn as_ref(&self) -> &[u8] {
&self.data[..]
}
}
@ -110,17 +108,17 @@ impl QuicDatagrams {
stats: &mut Stats,
) {
while let Some(dgram) = self.datagrams.pop_front() {
let len = dgram.len();
let len = dgram.as_ref().len();
if builder.remaining() > len {
// We need 1 more than `len` for the Frame type.
let length_len = Encoder::varint_len(u64::try_from(len).unwrap());
// Include a length if there is space for another frame after this one.
if builder.remaining() >= 1 + length_len + len + PacketBuilder::MINIMUM_FRAME_SIZE {
builder.encode_varint(FRAME_TYPE_DATAGRAM_WITH_LEN);
builder.encode_vvec(&dgram);
builder.encode_vvec(dgram.as_ref());
} else {
builder.encode_varint(FRAME_TYPE_DATAGRAM);
builder.encode(&dgram);
builder.encode(dgram.as_ref());
builder.mark_full();
}
debug_assert!(builder.len() <= builder.limit());

Просмотреть файл

@ -17,7 +17,7 @@ use std::time::{Duration, Instant};
use smallvec::{smallvec, SmallVec};
use neqo_common::{qdebug, qlog::NeqoQlog, qtrace, qwarn};
use neqo_common::{qdebug, qinfo, qlog::NeqoQlog, qtrace, qwarn};
use crate::ackrate::AckRate;
use crate::cid::ConnectionIdEntry;
@ -590,7 +590,7 @@ impl LossRecovery {
self.qlog = qlog;
}
pub fn drop_0rtt(&mut self, primary_path: &PathRef) -> Vec<SentPacket> {
pub fn drop_0rtt(&mut self, primary_path: &PathRef, now: Instant) -> Vec<SentPacket> {
// The largest acknowledged or loss_time should still be unset.
// The client should not have received any ACK frames when it drops 0-RTT.
assert!(self
@ -607,7 +607,7 @@ impl LossRecovery {
.collect::<Vec<_>>();
let mut path = primary_path.borrow_mut();
for p in &mut dropped {
path.discard_packet(p);
path.discard_packet(p, now);
}
dropped
}
@ -670,10 +670,14 @@ impl LossRecovery {
largest_acked
);
let space = self
.spaces
.get_mut(pn_space)
.expect("ACK on discarded space");
let space = self.spaces.get_mut(pn_space);
let space = if let Some(sp) = space {
sp
} else {
qinfo!("ACK on discarded space");
return (Vec::new(), Vec::new());
};
let (acked_packets, any_ack_eliciting) =
space.remove_acked(acked_ranges, &mut *self.stats.borrow_mut());
if acked_packets.is_empty() {
@ -737,7 +741,7 @@ impl LossRecovery {
/// When receiving a retry, get all the sent packets so that they can be flushed.
/// We also need to pretend that they never happened for the purposes of congestion control.
pub fn retry(&mut self, primary_path: &PathRef) -> Vec<SentPacket> {
pub fn retry(&mut self, primary_path: &PathRef, now: Instant) -> Vec<SentPacket> {
self.pto_state = None;
let mut dropped = self
.spaces
@ -746,7 +750,7 @@ impl LossRecovery {
.collect::<Vec<_>>();
let mut path = primary_path.borrow_mut();
for p in &mut dropped {
path.discard_packet(p);
path.discard_packet(p, now);
}
dropped
}
@ -779,7 +783,7 @@ impl LossRecovery {
qdebug!([self], "Reset loss recovery state for {}", space);
let mut path = primary_path.borrow_mut();
for p in self.spaces.drop_space(space) {
path.discard_packet(&p);
path.discard_packet(&p, now);
}
// We just made progress, so discard PTO count.
@ -1411,17 +1415,18 @@ mod tests {
}
#[test]
#[should_panic(expected = "ACK on discarded space")]
fn ack_after_drop() {
let mut lr = Fixture::default();
lr.discard(PacketNumberSpace::Initial, now());
lr.on_ack_received(
let (acked, lost) = lr.on_ack_received(
PacketNumberSpace::Initial,
0,
vec![],
Duration::from_millis(0),
pn_time(0),
);
assert!(acked.is_empty());
assert!(lost.is_empty());
}
#[test]
@ -1494,6 +1499,25 @@ mod tests {
#[test]
fn rearm_pto_after_confirmed() {
let mut lr = Fixture::default();
lr.on_packet_sent(SentPacket::new(
PacketType::Initial,
0,
now(),
true,
Vec::new(),
ON_SENT_SIZE,
));
// Set the RTT to the initial value so that discarding doesn't
// alter the estimate.
let rtt = lr.path.borrow().rtt().estimate();
lr.on_ack_received(
PacketNumberSpace::Initial,
0,
vec![0..=0],
Duration::new(0, 0),
now() + rtt,
);
lr.on_packet_sent(SentPacket::new(
PacketType::Handshake,
0,

Просмотреть файл

@ -1966,7 +1966,10 @@ mod tests {
&mut tokens,
&mut stats,
);
qtrace!("STREAM frame: {}", hex_with_len(&builder[header_len..]));
qtrace!(
"STREAM frame: {}",
hex_with_len(&builder.as_ref()[header_len..])
);
stats.stream > 0
}

44
third_party/rust/neqo-transport/src/server.rs поставляемый
Просмотреть файл

@ -20,7 +20,7 @@ use crate::addr_valid::{AddressValidation, AddressValidationResult};
use crate::cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdGenerator, ConnectionIdRef};
use crate::connection::{Connection, Output, State};
use crate::packet::{PacketBuilder, PacketType, PublicPacket};
use crate::{ConnectionParameters, QuicVersion, Res};
use crate::{ConnectionParameters, Res, Version};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet, VecDeque};
@ -109,7 +109,7 @@ struct InitialDetails {
src_cid: ConnectionId,
dst_cid: ConnectionId,
token: Vec<u8>,
quic_version: QuicVersion,
version: Version,
}
impl InitialDetails {
@ -118,7 +118,7 @@ impl InitialDetails {
src_cid: ConnectionId::from(packet.scid()),
dst_cid: ConnectionId::from(packet.dcid()),
token: packet.token().to_vec(),
quic_version: packet.version().unwrap(),
version: packet.version().unwrap(),
}
}
}
@ -339,7 +339,7 @@ impl Server {
};
if let Some(new_dcid) = self.cid_generator.borrow_mut().generate_cid() {
let packet = PacketBuilder::retry(
initial.quic_version,
initial.version,
&initial.src_cid,
&new_dcid,
&token,
@ -478,11 +478,13 @@ impl Server {
saved_cids: Vec::new(),
}));
let mut params = self.conn_params.clone();
params.get_versions_mut().set_initial(initial.version);
let sconn = Connection::new_server(
&self.certs,
&self.protocols,
Rc::clone(&cid_mgr) as _,
self.conn_params.quic_version(initial.quic_version),
params,
);
if let Ok(mut c) = sconn {
@ -554,6 +556,29 @@ impl Server {
return None;
}
if packet.packet_type() == PacketType::OtherVersion
|| (packet.packet_type() == PacketType::Initial
&& !self
.conn_params
.get_versions()
.all()
.contains(&packet.version().unwrap()))
{
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
qdebug!([self], "Unsupported version: too short");
return None;
}
qdebug!([self], "Unsupported version: {:x}", packet.wire_version());
let vn = PacketBuilder::version_negotiation(
packet.scid(),
packet.dcid(),
packet.wire_version(),
self.conn_params.get_versions().all(),
);
return Some(Datagram::new(dgram.destination(), dgram.source(), vn));
}
match packet.packet_type() {
PacketType::Initial => {
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
@ -568,14 +593,7 @@ impl Server {
let dcid = ConnectionId::from(packet.dcid());
self.handle_0rtt(dgram, dcid, now)
}
PacketType::OtherVersion => {
if dgram.len() < MIN_INITIAL_PACKET_SIZE {
qdebug!([self], "Unsupported version: too short");
return None;
}
let vn = PacketBuilder::version_negotiation(packet.scid(), packet.dcid());
Some(Datagram::new(dgram.destination(), dgram.source(), vn))
}
PacketType::OtherVersion => unreachable!(),
_ => {
qtrace!([self], "Not an initial packet");
None

Просмотреть файл

@ -18,7 +18,7 @@ use std::time::Duration;
pub(crate) const MAX_PTO_COUNTS: usize = 16;
#[derive(Default, Clone)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[allow(clippy::module_name_repetitions)]
pub struct FrameStats {
pub all: usize,

303
third_party/rust/neqo-transport/src/tparams.rs поставляемый
Просмотреть файл

@ -9,12 +9,13 @@
use crate::cid::{
ConnectionId, ConnectionIdEntry, CONNECTION_ID_SEQNO_PREFERRED, MAX_CONNECTION_ID_LEN,
};
use crate::version::{Version, VersionConfig, WireVersion};
use crate::{Error, Res};
use neqo_common::{hex, qdebug, qinfo, qtrace, Decoder, Encoder};
use neqo_common::{hex, qdebug, qinfo, qtrace, Decoder, Encoder, Role};
use neqo_crypto::constants::{TLS_HS_CLIENT_HELLO, TLS_HS_ENCRYPTED_EXTENSIONS};
use neqo_crypto::ext::{ExtensionHandler, ExtensionHandlerResult, ExtensionWriterResult};
use neqo_crypto::{HandshakeMessage, ZeroRttCheckResult, ZeroRttChecker};
use neqo_crypto::{random, HandshakeMessage, ZeroRttCheckResult, ZeroRttChecker};
use std::cell::RefCell;
use std::collections::HashMap;
@ -26,6 +27,10 @@ pub type TransportParameterId = u64;
macro_rules! tpids {
{ $($n:ident = $v:expr),+ $(,)? } => {
$(pub const $n: TransportParameterId = $v as TransportParameterId;)+
/// A complete list of internal transport parameters.
#[cfg(not(test))]
pub(crate) const INTERNAL_TRANSPORT_PARAMETERS: &[TransportParameterId] = &[ $($n),+ ];
};
}
tpids! {
@ -49,6 +54,7 @@ tpids! {
GREASE_QUIC_BIT = 0x2ab2,
MIN_ACK_DELAY = 0xff02_de1a,
MAX_DATAGRAM_FRAME_SIZE = 0x0020,
VERSION_NEGOTIATION = 0xff73db,
}
#[derive(Clone, Debug, Copy)]
@ -94,7 +100,7 @@ impl PreferredAddress {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TransportParameter {
Bytes(Vec<u8>),
Integer(u64),
@ -105,6 +111,10 @@ pub enum TransportParameter {
cid: ConnectionId,
srt: [u8; 16],
},
Versions {
current: WireVersion,
other: Vec<WireVersion>,
},
}
impl TransportParameter {
@ -151,6 +161,14 @@ impl TransportParameter {
enc_inner.encode(&srt[..]);
});
}
Self::Versions { current, other } => {
enc.encode_vvec_with(|enc_inner| {
enc_inner.encode_uint(4, *current);
for v in other {
enc_inner.encode_uint(4, *v);
}
});
}
};
}
@ -199,6 +217,26 @@ impl TransportParameter {
Ok(Self::PreferredAddress { v4, v6, cid, srt })
}
fn decode_versions(dec: &mut Decoder) -> Res<Self> {
fn dv(dec: &mut Decoder) -> Res<WireVersion> {
let v = dec.decode_uint(4).ok_or(Error::NoMoreData)?;
if v == 0 {
Err(Error::TransportParameterError)
} else {
Ok(v as WireVersion)
}
}
let current = dv(dec)?;
// This rounding down is OK because `decode` checks for left over data.
let count = dec.remaining() / 4;
let mut other = Vec::with_capacity(count);
for _ in 0..count {
other.push(dv(dec)?);
}
Ok(Self::Versions { current, other })
}
fn decode(dec: &mut Decoder) -> Res<Option<(TransportParameterId, Self)>> {
let tp = dec.decode_varint().ok_or(Error::NoMoreData)?;
let content = dec.decode_vvec().ok_or(Error::NoMoreData)?;
@ -253,6 +291,8 @@ impl TransportParameter {
_ => return Err(Error::TransportParameterError),
},
VERSION_NEGOTIATION => Self::decode_versions(&mut d)?,
// Skip.
_ => return Ok(None),
};
@ -264,7 +304,7 @@ impl TransportParameter {
}
}
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TransportParameters {
params: HashMap<TransportParameterId, TransportParameter>,
}
@ -388,6 +428,37 @@ impl TransportParameters {
}
}
/// Set version information.
pub fn set_versions(&mut self, role: Role, versions: &VersionConfig) {
let rbuf = random(4);
let mut other = Vec::with_capacity(versions.all().len() + 1);
let mut dec = Decoder::new(&rbuf);
let grease = (dec.decode_uint(4).unwrap() as u32) & 0xf0f0_f0f0 | 0x0a0a0a0a;
other.push(grease);
for &v in versions.all() {
if role == Role::Client && !versions.initial().is_compatible(v) {
continue;
}
other.push(v.wire_version());
}
let current = versions.initial().wire_version();
self.set(
VERSION_NEGOTIATION,
TransportParameter::Versions { current, other },
);
}
fn compatible_upgrade(&mut self, v: Version) {
if let Some(TransportParameter::Versions {
ref mut current, ..
}) = self.params.get_mut(&VERSION_NEGOTIATION)
{
*current = v.wire_version();
} else {
unreachable!("Compatible upgrade without transport parameters set!");
}
}
pub fn get_empty(&self, tipe: TransportParameterId) -> bool {
match self.params.get(&tipe) {
None => false,
@ -416,23 +487,30 @@ impl TransportParameters {
) {
continue;
}
if let Some(v_self) = self.params.get(k) {
let ok = if let Some(v_self) = self.params.get(k) {
match (v_self, v_rem) {
(TransportParameter::Integer(i_self), TransportParameter::Integer(i_rem)) => {
if *k == MIN_ACK_DELAY {
// MIN_ACK_DELAY is backwards:
// it can only be reduced safely.
if *i_self > *i_rem {
return false;
}
} else if *i_self < *i_rem {
return false;
*i_self <= *i_rem
} else {
*i_self >= *i_rem
}
}
(TransportParameter::Empty, TransportParameter::Empty) => {}
_ => return false,
(TransportParameter::Empty, TransportParameter::Empty) => true,
(
TransportParameter::Versions {
current: v_self, ..
},
TransportParameter::Versions { current: v_rem, .. },
) => v_self == v_rem,
_ => false,
}
} else {
false
};
if !ok {
return false;
}
}
@ -454,26 +532,117 @@ impl TransportParameters {
}
}
/// Get the version negotiation values for validation.
#[must_use]
pub fn get_versions(&self) -> Option<(WireVersion, &[WireVersion])> {
if let Some(TransportParameter::Versions { current, other }) =
self.params.get(&VERSION_NEGOTIATION)
{
Some((*current, other))
} else {
None
}
}
#[must_use]
pub fn has_value(&self, tp: TransportParameterId) -> bool {
self.params.contains_key(&tp)
}
}
#[derive(Default, Debug)]
#[derive(Debug)]
pub struct TransportParametersHandler {
role: Role,
versions: VersionConfig,
pub(crate) local: TransportParameters,
pub(crate) remote: Option<TransportParameters>,
pub(crate) remote_0rtt: Option<TransportParameters>,
}
impl TransportParametersHandler {
pub fn new(role: Role, versions: VersionConfig) -> Self {
let mut local = TransportParameters::default();
local.set_versions(role, &versions);
Self {
role,
versions,
local,
remote: None,
remote_0rtt: None,
}
}
/// When resuming, the version is set based on the ticket.
/// That needs to be done to override the default choice from configuration.
pub fn set_version(&mut self, version: Version) {
debug_assert_eq!(self.role, Role::Client);
self.versions.set_initial(version);
self.local.set_versions(self.role, &self.versions)
}
pub fn remote(&self) -> &TransportParameters {
match (self.remote.as_ref(), self.remote_0rtt.as_ref()) {
(Some(tp), _) | (_, Some(tp)) => tp,
_ => panic!("no transport parameters from peer"),
}
}
/// Get the version as set (or as determined by a compatible upgrade).
pub fn version(&self) -> Version {
self.versions.initial()
}
fn compatible_upgrade(&mut self, remote_tp: &TransportParameters) -> Res<()> {
if let Some((current, other)) = remote_tp.get_versions() {
qtrace!(
"Peer versions: {:x} {:x?}; config {:?}",
current,
other,
self.versions,
);
if self.role == Role::Client {
let chosen = Version::try_from(current)?;
if self.versions.compatible().any(|&v| v == chosen) {
Ok(())
} else {
qinfo!(
"Chosen version {:x} is not compatible with initial version {:x}",
current,
self.versions.initial().wire_version(),
);
Err(Error::TransportParameterError)
}
} else {
if current != self.versions.initial().wire_version() {
qinfo!(
"Current version {:x} != own version {:x}",
current,
self.versions.initial().wire_version(),
);
return Err(Error::TransportParameterError);
}
if let Some(preferred) = self.versions.preferred_compatible(other) {
if preferred != self.versions.initial() {
qinfo!(
"Compatible upgrade {:?} ==> {:?}",
self.versions.initial(),
preferred
);
self.versions.set_initial(preferred);
self.local.compatible_upgrade(preferred);
}
Ok(())
} else {
qinfo!("Unable to find any compatible version");
Err(Error::TransportParameterError)
}
}
} else {
Ok(())
}
}
}
impl ExtensionHandler for TransportParametersHandler {
@ -488,7 +657,7 @@ impl ExtensionHandler for TransportParametersHandler {
let mut enc = Encoder::default();
self.local.encode(&mut enc);
assert!(enc.len() <= d.len());
d[..enc.len()].copy_from_slice(&enc);
d[..enc.len()].copy_from_slice(enc.as_ref());
ExtensionWriterResult::Write(enc.len())
}
@ -506,8 +675,12 @@ impl ExtensionHandler for TransportParametersHandler {
let mut dec = Decoder::from(d);
match TransportParameters::decode(&mut dec) {
Ok(tp) => {
self.remote = Some(tp);
ExtensionHandlerResult::Ok
if self.compatible_upgrade(&tp).is_ok() {
self.remote = Some(tp);
ExtensionHandlerResult::Ok
} else {
ExtensionHandlerResult::Alert(47)
}
}
_ => ExtensionHandlerResult::Alert(47), // illegal_parameter
}
@ -569,7 +742,6 @@ where
}
}
// TODO(ekr@rtfm.com): Need to write more TP unit tests.
#[cfg(test)]
#[allow(unused_variables)]
mod tests {
@ -639,7 +811,7 @@ mod tests {
let spa = make_spa();
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
assert_eq!(&enc[..], ENCODED);
assert_eq!(enc.as_ref(), ENCODED);
let mut dec = enc.as_decoder();
let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap();
@ -733,7 +905,7 @@ mod tests {
let spa = make_spa();
let mut enc = Encoder::new();
spa.encode(&mut enc, PREFERRED_ADDRESS);
let mut dec = Decoder::from(&enc[..enc.len() - 1]);
let mut dec = Decoder::from(&enc.as_ref()[..enc.len() - 1]);
assert_eq!(
TransportParameter::decode(&mut dec).unwrap_err(),
Error::NoMoreData
@ -911,4 +1083,97 @@ mod tests {
let invalid_decode_result = TransportParameters::decode(&mut enc.as_decoder());
assert!(invalid_decode_result.is_err());
}
#[test]
fn versions_encode_decode() {
const ENCODED: &[u8] = &[
0x80, 0xff, 0x73, 0xdb, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a,
0x6a, 0x7a, 0x8a,
];
let vn = TransportParameter::Versions {
current: Version::Version1.wire_version(),
other: vec![0x1a2a_3a4a, 0x5a6a_7a8a],
};
let mut enc = Encoder::new();
vn.encode(&mut enc, VERSION_NEGOTIATION);
assert_eq!(enc.as_ref(), ENCODED);
let mut dec = enc.as_decoder();
let (id, decoded) = TransportParameter::decode(&mut dec).unwrap().unwrap();
assert_eq!(id, VERSION_NEGOTIATION);
assert_eq!(decoded, vn);
}
#[test]
fn versions_truncated() {
const TRUNCATED: &[u8] = &[
0x80, 0xff, 0x73, 0xdb, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a,
0x6a, 0x7a,
];
let mut dec = Decoder::from(&TRUNCATED);
assert_eq!(
TransportParameter::decode(&mut dec).unwrap_err(),
Error::NoMoreData
);
}
#[test]
fn versions_zero() {
const ZERO1: &[u8] = &[0x80, 0xff, 0x73, 0xdb, 0x04, 0x00, 0x00, 0x00, 0x00];
const ZERO2: &[u8] = &[
0x80, 0xff, 0x73, 0xdb, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
];
let mut dec = Decoder::from(&ZERO1);
assert_eq!(
TransportParameter::decode(&mut dec).unwrap_err(),
Error::TransportParameterError
);
let mut dec = Decoder::from(&ZERO2);
assert_eq!(
TransportParameter::decode(&mut dec).unwrap_err(),
Error::TransportParameterError
);
}
#[test]
fn versions_equal_0rtt() {
let mut current = TransportParameters::default();
current.set(
VERSION_NEGOTIATION,
TransportParameter::Versions {
current: Version::Version1.wire_version(),
other: vec![0x1a2a_3a4a],
},
);
let mut remembered = TransportParameters::default();
// It's OK to not remember having versions.
assert!(current.ok_for_0rtt(&remembered));
// But it is bad in the opposite direction.
assert!(!remembered.ok_for_0rtt(&current));
// If the version matches, it's OK to use 0-RTT.
remembered.set(
VERSION_NEGOTIATION,
TransportParameter::Versions {
current: Version::Version1.wire_version(),
other: vec![0x5a6a_7a8a, 0x9aaa_baca],
},
);
assert!(current.ok_for_0rtt(&remembered));
assert!(remembered.ok_for_0rtt(&current));
// An apparent "upgrade" is still cause to reject 0-RTT.
remembered.set(
VERSION_NEGOTIATION,
TransportParameter::Versions {
current: Version::Version1.wire_version() + 1,
other: vec![],
},
);
assert!(!current.ok_for_0rtt(&remembered));
assert!(!remembered.ok_for_0rtt(&current));
}
}

233
third_party/rust/neqo-transport/src/version.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,233 @@
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{Error, Res};
use neqo_common::qdebug;
use std::convert::TryFrom;
pub type WireVersion = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Version {
Version2,
Version1,
Draft29,
Draft30,
Draft31,
Draft32,
}
impl Version {
pub const fn wire_version(self) -> WireVersion {
match self {
Self::Version2 => 0x709a50c4,
Self::Version1 => 1,
Self::Draft29 => 0xff00_0000 + 29,
Self::Draft30 => 0xff00_0000 + 30,
Self::Draft31 => 0xff00_0000 + 31,
Self::Draft32 => 0xff00_0000 + 32,
}
}
pub(crate) fn initial_salt(self) -> &'static [u8] {
const INITIAL_SALT_V2: &[u8] = &[
0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04,
0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3,
];
const INITIAL_SALT_V1: &[u8] = &[
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
];
const INITIAL_SALT_29_32: &[u8] = &[
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
];
match self {
Self::Version2 => INITIAL_SALT_V2,
Self::Version1 => INITIAL_SALT_V1,
Self::Draft29 | Self::Draft30 | Self::Draft31 | Self::Draft32 => INITIAL_SALT_29_32,
}
}
pub(crate) fn label_prefix(self) -> &'static str {
match self {
Self::Version2 => "quicv2 ",
Self::Version1 | Self::Draft29 | Self::Draft30 | Self::Draft31 | Self::Draft32 => {
"quic "
}
}
}
pub(crate) fn retry_secret(self) -> &'static [u8] {
const RETRY_SECRET_29: &[u8] = &[
0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8,
0x0d, 0xf2, 0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa,
0x2b, 0xaa, 0xff, 0x4c,
];
const RETRY_SECRET_V1: &[u8] = &[
0xd9, 0xc9, 0x94, 0x3e, 0x61, 0x01, 0xfd, 0x20, 0x00, 0x21, 0x50, 0x6b, 0xcc, 0x02,
0x81, 0x4c, 0x73, 0x03, 0x0f, 0x25, 0xc7, 0x9d, 0x71, 0xce, 0x87, 0x6e, 0xca, 0x87,
0x6e, 0x6f, 0xca, 0x8e,
];
const RETRY_SECRET_V2: &[u8] = &[
0x34, 0x25, 0xc2, 0x0c, 0xf8, 0x87, 0x79, 0xdf, 0x2f, 0xf7, 0x1e, 0x8a, 0xbf, 0xa7,
0x82, 0x49, 0x89, 0x1e, 0x76, 0x3b, 0xbe, 0xd2, 0xf1, 0x3c, 0x04, 0x83, 0x43, 0xd3,
0x48, 0xc0, 0x60, 0xe2,
];
match self {
Self::Version2 => RETRY_SECRET_V2,
Self::Version1 => RETRY_SECRET_V1,
Self::Draft29 | Self::Draft30 | Self::Draft31 | Self::Draft32 => RETRY_SECRET_29,
}
}
pub(crate) fn is_draft(self) -> bool {
matches!(
self,
Self::Draft29 | Self::Draft30 | Self::Draft31 | Self::Draft32,
)
}
/// Determine if `self` can be upgraded to `other` compatibly.
pub fn is_compatible(self, other: Self) -> bool {
self == other
|| matches!(
(self, other),
(Self::Version1, Self::Version2) | (Self::Version2, Self::Version1)
)
}
pub fn all() -> Vec<Self> {
vec![
Self::Version2,
Self::Version1,
Self::Draft32,
Self::Draft31,
Self::Draft30,
Self::Draft29,
]
}
pub fn compatible<'a>(
self,
all: impl IntoIterator<Item = &'a Self>,
) -> impl Iterator<Item = &'a Self> {
all.into_iter().filter(move |&v| self.is_compatible(*v))
}
}
impl Default for Version {
fn default() -> Self {
Self::Version1
}
}
impl TryFrom<WireVersion> for Version {
type Error = Error;
fn try_from(wire: WireVersion) -> Res<Self> {
if wire == 1 {
Ok(Self::Version1)
} else if wire == 0x709a50c4 {
Ok(Self::Version2)
} else if wire == 0xff00_0000 + 29 {
Ok(Self::Draft29)
} else if wire == 0xff00_0000 + 30 {
Ok(Self::Draft30)
} else if wire == 0xff00_0000 + 31 {
Ok(Self::Draft31)
} else if wire == 0xff00_0000 + 32 {
Ok(Self::Draft32)
} else {
Err(Error::VersionNegotiation)
}
}
}
#[derive(Debug, Clone)]
pub struct VersionConfig {
/// The version that a client uses to establish a connection.
///
/// For a client, this is the version that is sent out in an Initial packet.
/// A client that resumes will set this to the version from the original
/// connection.
/// A client that handles a Version Negotiation packet will be initialized with
/// a version chosen from the packet, but it will then have this value overridden
/// to match the original configuration so that the version negotiation can be
/// authenticated.
///
/// For a server `Connection`, this is the only type of Initial packet that
/// can be accepted; the correct value is set by `Server`, see below.
///
/// For a `Server`, this value is not used; if an Initial packet is received
/// in a supported version (as listed in `versions`), new instances of
/// `Connection` will be created with this value set to match what was received.
///
/// An invariant here is that this version is always listed in `all`.
initial: Version,
/// The set of versions that are enabled, in preference order. For a server,
/// only the relative order of compatible versions matters.
all: Vec<Version>,
}
impl VersionConfig {
pub fn new(initial: Version, all: Vec<Version>) -> Self {
assert!(all.contains(&initial));
Self { initial, all }
}
pub fn initial(&self) -> Version {
self.initial
}
pub fn all(&self) -> &[Version] {
&self.all
}
/// Overwrite the initial value; used by the `Server` when handling new connections
/// and by the client on resumption.
pub(crate) fn set_initial(&mut self, initial: Version) {
qdebug!(
"Overwrite initial version {:?} ==> {:?}",
self.initial,
initial
);
assert!(self.all.contains(&initial));
self.initial = initial;
}
pub fn compatible(&self) -> impl Iterator<Item = &Version> {
self.initial.compatible(&self.all)
}
fn find_preferred<'a>(
preferences: impl IntoIterator<Item = &'a Version>,
vn: &[WireVersion],
) -> Option<Version> {
for v in preferences {
if vn.contains(&v.wire_version()) {
return Some(*v);
}
}
None
}
/// Determine the preferred version based on a version negotiation packet.
pub(crate) fn preferred(&self, vn: &[WireVersion]) -> Option<Version> {
Self::find_preferred(&self.all, vn)
}
/// Determine the preferred version based on a set of compatible versions.
pub(crate) fn preferred_compatible(&self, vn: &[WireVersion]) -> Option<Version> {
Self::find_preferred(self.compatible(), vn)
}
}
impl Default for VersionConfig {
fn default() -> Self {
Self::new(Version::default(), Version::all())
}
}

Просмотреть файл

@ -6,8 +6,9 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(clippy::pedantic)]
#![allow(unused)]
use neqo_common::{event::Provider, hex_with_len, qtrace, Datagram, Decoder};
use neqo_common::{event::Provider, hex_with_len, qtrace, Datagram, Decoder, Role};
use neqo_crypto::{
constants::{TLS_AES_128_GCM_SHA256, TLS_VERSION_1_3},
hkdf,
@ -26,8 +27,8 @@ use std::mem;
use std::ops::Range;
use std::rc::Rc;
// Different than the one in the fixture, which is a single connection.
pub fn default_server() -> Server {
/// Create a server. This is different than the one in the fixture, which is a single connection.
pub fn new_server(params: ConnectionParameters) -> Server {
Server::new(
now(),
test_fixture::DEFAULT_KEYS,
@ -35,11 +36,16 @@ pub fn default_server() -> Server {
test_fixture::anti_replay(),
Box::new(AllowZeroRtt {}),
Rc::new(RefCell::new(CountingConnectionIdGenerator::default())),
ConnectionParameters::default(),
params,
)
.expect("should create a server")
}
/// Create a server. This is different than the one in the fixture, which is a single connection.
pub fn default_server() -> Server {
new_server(ConnectionParameters::default())
}
// Check that there is at least one connection. Returns a ref to the first confirmed connection.
pub fn connected_server(server: &mut Server) -> ActiveConnectionRef {
let server_connections = server.active_connections();
@ -91,10 +97,14 @@ pub fn connect(client: &mut Connection, server: &mut Server) -> ActiveConnection
// * the protected payload including the packet number.
// Any token is thrown away.
#[must_use]
pub fn decode_initial_header(dgram: &Datagram) -> (&[u8], &[u8], &[u8], &[u8]) {
pub fn decode_initial_header(dgram: &Datagram, role: Role) -> (&[u8], &[u8], &[u8], &[u8]) {
let mut dec = Decoder::new(&dgram[..]);
let type_and_ver = dec.decode(5).unwrap().to_vec();
assert_eq!(type_and_ver[0] & 0xf0, 0xc0);
// The client sets the QUIC bit, the server might not.
match role {
Role::Client => assert_eq!(type_and_ver[0] & 0xf0, 0xc0),
Role::Server => assert_eq!(type_and_ver[0] & 0xb0, 0x80),
}
let dest_cid = dec.decode_vec(1).unwrap();
let src_cid = dec.decode_vec(1).unwrap();
dec.skip_vvec(); // Ignore any the token.
@ -110,9 +120,10 @@ pub fn decode_initial_header(dgram: &Datagram) -> (&[u8], &[u8], &[u8], &[u8]) {
)
}
// Generate an AEAD and header protection object for a client Initial.
/// Generate an AEAD and header protection object for a client Initial.
/// Note that this works for QUIC version 1 only.
#[must_use]
pub fn client_initial_aead_and_hp(dcid: &[u8]) -> (Aead, HpKey) {
pub fn initial_aead_and_hp(dcid: &[u8], role: Role) -> (Aead, HpKey) {
const INITIAL_SALT: &[u8] = &[
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c,
0xad, 0xcc, 0xbb, 0x7f, 0x0a,
@ -134,7 +145,10 @@ pub fn client_initial_aead_and_hp(dcid: &[u8]) -> (Aead, HpKey) {
TLS_AES_128_GCM_SHA256,
&initial_secret,
&[],
"client in",
match role {
Role::Client => "client in",
Role::Server => "server in",
},
)
.unwrap();
(
@ -184,15 +198,9 @@ pub fn apply_header_protection(hp: &HpKey, packet: &mut [u8], pn_bytes: Range<us
}
}
pub fn get_ticket(server: &mut Server) -> ResumptionToken {
let mut client = default_client();
let mut server_conn = connect(&mut client, server);
server_conn.borrow_mut().send_ticket(now(), &[]).unwrap();
let dgram = server.process(None, now()).dgram();
client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output.
let ticket = client
/// Scrub through client events to find a resumption token.
pub fn find_ticket(client: &mut Connection) -> ResumptionToken {
client
.events()
.find_map(|e| {
if let ConnectionEvent::ResumptionToken(token) = e {
@ -201,7 +209,18 @@ pub fn get_ticket(server: &mut Server) -> ResumptionToken {
None
}
})
.unwrap();
.unwrap()
}
/// Connect to the server and have it generate a ticket.
pub fn generate_ticket(server: &mut Server) -> ResumptionToken {
let mut client = default_client();
let mut server_conn = connect(&mut client, server);
server_conn.borrow_mut().send_ticket(now(), &[]).unwrap();
let dgram = server.process(None, now()).dgram();
client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output.
let ticket = find_ticket(&mut client);
// Have the client close the connection and then let the server clean up.
client.close(now(), 0, "got a ticket");

Просмотреть файл

@ -10,13 +10,91 @@
use neqo_common::Datagram;
use neqo_transport::{
Connection, ConnectionParameters, QuicVersion, RandomConnectionIdGenerator, State,
Connection, ConnectionParameters, RandomConnectionIdGenerator, State, Version,
};
use test_fixture::{self, addr, now};
use std::cell::RefCell;
use std::rc::Rc;
const INITIAL_PACKET_V2: &[u8] = &[
0xdd, 0x70, 0x9a, 0x50, 0xc4, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
0x44, 0x9e, 0x43, 0x91, 0xd8, 0x48, 0x23, 0xb8, 0xe6, 0x10, 0x58, 0x9c, 0x83, 0xc9, 0x2d, 0x0e,
0x97, 0xeb, 0x7a, 0x6e, 0x50, 0x03, 0xf5, 0x77, 0x64, 0xc5, 0xc7, 0xf0, 0x09, 0x5b, 0xa5, 0x4b,
0x90, 0x81, 0x8f, 0x1b, 0xfe, 0xec, 0xc1, 0xc9, 0x7c, 0x54, 0xfc, 0x73, 0x1e, 0xdb, 0xd2, 0xa2,
0x44, 0xe3, 0xb1, 0xe6, 0x39, 0xa9, 0xbc, 0x75, 0xed, 0x54, 0x5b, 0x98, 0x64, 0x93, 0x43, 0xb2,
0x53, 0x61, 0x5e, 0xc6, 0xb3, 0xe4, 0xdf, 0x0f, 0xd2, 0xe7, 0xfe, 0x9d, 0x69, 0x1a, 0x09, 0xe6,
0xa1, 0x44, 0xb4, 0x36, 0xd8, 0xa2, 0xc0, 0x88, 0xa4, 0x04, 0x26, 0x23, 0x40, 0xdf, 0xd9, 0x95,
0xec, 0x38, 0x65, 0x69, 0x4e, 0x30, 0x26, 0xec, 0xd8, 0xc6, 0xd2, 0x56, 0x1a, 0x5a, 0x36, 0x67,
0x2a, 0x10, 0x05, 0x01, 0x81, 0x68, 0xc0, 0xf0, 0x81, 0xc1, 0x0e, 0x2b, 0xf1, 0x4d, 0x55, 0x0c,
0x97, 0x7e, 0x28, 0xbb, 0x9a, 0x75, 0x9c, 0x57, 0xd0, 0xf7, 0xff, 0xb1, 0xcd, 0xfb, 0x40, 0xbd,
0x77, 0x4d, 0xec, 0x58, 0x96, 0x57, 0x54, 0x20, 0x47, 0xdf, 0xfe, 0xfa, 0x56, 0xfc, 0x80, 0x89,
0xa4, 0xd1, 0xef, 0x37, 0x9c, 0x81, 0xba, 0x3d, 0xf7, 0x1a, 0x05, 0xdd, 0xc7, 0x92, 0x83, 0x40,
0x77, 0x59, 0x10, 0xfe, 0xb3, 0xce, 0x4c, 0xbc, 0xfd, 0x8d, 0x25, 0x3e, 0xdd, 0x05, 0xf1, 0x61,
0x45, 0x8f, 0x9d, 0xc4, 0x4b, 0xea, 0x01, 0x7c, 0x31, 0x17, 0xcc, 0xa7, 0x06, 0x5a, 0x31, 0x5d,
0xed, 0xa9, 0x46, 0x4e, 0x67, 0x2e, 0xc8, 0x0c, 0x3f, 0x79, 0xac, 0x99, 0x34, 0x37, 0xb4, 0x41,
0xef, 0x74, 0x22, 0x7e, 0xcc, 0x4d, 0xc9, 0xd5, 0x97, 0xf6, 0x6a, 0xb0, 0xab, 0x8d, 0x21, 0x4b,
0x55, 0x84, 0x0c, 0x70, 0x34, 0x9d, 0x76, 0x16, 0xcb, 0xe3, 0x8e, 0x5e, 0x1d, 0x05, 0x2d, 0x07,
0xf1, 0xfe, 0xdb, 0x3d, 0xd3, 0xc4, 0xd8, 0xce, 0x29, 0x57, 0x24, 0x94, 0x5e, 0x67, 0xed, 0x2e,
0xef, 0xcd, 0x9f, 0xb5, 0x24, 0x72, 0x38, 0x7f, 0x31, 0x8e, 0x3d, 0x9d, 0x23, 0x3b, 0xe7, 0xdf,
0xc7, 0x9d, 0x6b, 0xf6, 0x08, 0x0d, 0xcb, 0xbb, 0x41, 0xfe, 0xb1, 0x80, 0xd7, 0x85, 0x88, 0x49,
0x7c, 0x3e, 0x43, 0x9d, 0x38, 0xc3, 0x34, 0x74, 0x8d, 0x2b, 0x56, 0xfd, 0x19, 0xab, 0x36, 0x4d,
0x05, 0x7a, 0x9b, 0xd5, 0xa6, 0x99, 0xae, 0x14, 0x5d, 0x7f, 0xdb, 0xc8, 0xf5, 0x77, 0x75, 0x18,
0x1b, 0x0a, 0x97, 0xc3, 0xbd, 0xed, 0xc9, 0x1a, 0x55, 0x5d, 0x6c, 0x9b, 0x86, 0x34, 0xe1, 0x06,
0xd8, 0xc9, 0xca, 0x45, 0xa9, 0xd5, 0x45, 0x0a, 0x76, 0x79, 0xed, 0xc5, 0x45, 0xda, 0x91, 0x02,
0x5b, 0xc9, 0x3a, 0x7c, 0xf9, 0xa0, 0x23, 0xa0, 0x66, 0xff, 0xad, 0xb9, 0x71, 0x7f, 0xfa, 0xf3,
0x41, 0x4c, 0x3b, 0x64, 0x6b, 0x57, 0x38, 0xb3, 0xcc, 0x41, 0x16, 0x50, 0x2d, 0x18, 0xd7, 0x9d,
0x82, 0x27, 0x43, 0x63, 0x06, 0xd9, 0xb2, 0xb3, 0xaf, 0xc6, 0xc7, 0x85, 0xce, 0x3c, 0x81, 0x7f,
0xeb, 0x70, 0x3a, 0x42, 0xb9, 0xc8, 0x3b, 0x59, 0xf0, 0xdc, 0xef, 0x12, 0x45, 0xd0, 0xb3, 0xe4,
0x02, 0x99, 0x82, 0x1e, 0xc1, 0x95, 0x49, 0xce, 0x48, 0x97, 0x14, 0xfe, 0x26, 0x11, 0xe7, 0x2c,
0xd8, 0x82, 0xf4, 0xf7, 0x0d, 0xce, 0x7d, 0x36, 0x71, 0x29, 0x6f, 0xc0, 0x45, 0xaf, 0x5c, 0x9f,
0x63, 0x0d, 0x7b, 0x49, 0xa3, 0xeb, 0x82, 0x1b, 0xbc, 0xa6, 0x0f, 0x19, 0x84, 0xdc, 0xe6, 0x64,
0x91, 0x71, 0x3b, 0xfe, 0x06, 0x00, 0x1a, 0x56, 0xf5, 0x1b, 0xb3, 0xab, 0xe9, 0x2f, 0x79, 0x60,
0x54, 0x7c, 0x4d, 0x0a, 0x70, 0xf4, 0xa9, 0x62, 0xb3, 0xf0, 0x5d, 0xc2, 0x5a, 0x34, 0xbb, 0xe8,
0x30, 0xa7, 0xea, 0x47, 0x36, 0xd3, 0xb0, 0x16, 0x17, 0x23, 0x50, 0x0d, 0x82, 0xbe, 0xda, 0x9b,
0xe3, 0x32, 0x7a, 0xf2, 0xaa, 0x41, 0x38, 0x21, 0xff, 0x67, 0x8b, 0x2a, 0x87, 0x6e, 0xc4, 0xb0,
0x0b, 0xb6, 0x05, 0xff, 0xcc, 0x39, 0x17, 0xff, 0xdc, 0x27, 0x9f, 0x18, 0x7d, 0xaa, 0x2f, 0xce,
0x8c, 0xde, 0x12, 0x19, 0x80, 0xbb, 0xa8, 0xec, 0x8f, 0x44, 0xca, 0x56, 0x2b, 0x0f, 0x13, 0x19,
0x14, 0xc9, 0x01, 0xcf, 0xbd, 0x84, 0x74, 0x08, 0xb7, 0x78, 0xe6, 0x73, 0x8c, 0x7b, 0xb5, 0xb1,
0xb3, 0xf9, 0x7d, 0x01, 0xb0, 0xa2, 0x4d, 0xcc, 0xa4, 0x0e, 0x3b, 0xed, 0x29, 0x41, 0x1b, 0x1b,
0xa8, 0xf6, 0x08, 0x43, 0xc4, 0xa2, 0x41, 0x02, 0x1b, 0x23, 0x13, 0x2b, 0x95, 0x00, 0x50, 0x9b,
0x9a, 0x35, 0x16, 0xd4, 0xa9, 0xdd, 0x41, 0xd3, 0xba, 0xcb, 0xcd, 0x42, 0x6b, 0x45, 0x13, 0x93,
0x52, 0x18, 0x28, 0xaf, 0xed, 0xcf, 0x20, 0xfa, 0x46, 0xac, 0x24, 0xf4, 0x4a, 0x8e, 0x29, 0x73,
0x30, 0xb1, 0x67, 0x05, 0xd5, 0xd5, 0xf7, 0x98, 0xef, 0xf9, 0xe9, 0x13, 0x4a, 0x06, 0x59, 0x79,
0x87, 0xa1, 0xdb, 0x46, 0x17, 0xca, 0xa2, 0xd9, 0x38, 0x37, 0x73, 0x08, 0x29, 0xd4, 0xd8, 0x9e,
0x16, 0x41, 0x3b, 0xe4, 0xd8, 0xa8, 0xa3, 0x8a, 0x7e, 0x62, 0x26, 0x62, 0x3b, 0x64, 0xa8, 0x20,
0x17, 0x8e, 0xc3, 0xa6, 0x69, 0x54, 0xe1, 0x07, 0x10, 0xe0, 0x43, 0xae, 0x73, 0xdd, 0x3f, 0xb2,
0x71, 0x5a, 0x05, 0x25, 0xa4, 0x63, 0x43, 0xfb, 0x75, 0x90, 0xe5, 0xea, 0xc7, 0xee, 0x55, 0xfc,
0x81, 0x0e, 0x0d, 0x8b, 0x4b, 0x8f, 0x7b, 0xe8, 0x2c, 0xd5, 0xa2, 0x14, 0x57, 0x5a, 0x1b, 0x99,
0x62, 0x9d, 0x47, 0xa9, 0xb2, 0x81, 0xb6, 0x13, 0x48, 0xc8, 0x62, 0x7c, 0xab, 0x38, 0xe2, 0xa6,
0x4d, 0xb6, 0x62, 0x6e, 0x97, 0xbb, 0x8f, 0x77, 0xbd, 0xcb, 0x0f, 0xee, 0x47, 0x6a, 0xed, 0xd7,
0xba, 0x8f, 0x54, 0x41, 0xac, 0xaa, 0xb0, 0x0f, 0x44, 0x32, 0xed, 0xab, 0x37, 0x91, 0x04, 0x7d,
0x90, 0x91, 0xb2, 0xa7, 0x53, 0xf0, 0x35, 0x64, 0x84, 0x31, 0xf6, 0xd1, 0x2f, 0x7d, 0x6a, 0x68,
0x1e, 0x64, 0xc8, 0x61, 0xf4, 0xac, 0x91, 0x1a, 0x0f, 0x7d, 0x6e, 0xc0, 0x49, 0x1a, 0x78, 0xc9,
0xf1, 0x92, 0xf9, 0x6b, 0x3a, 0x5e, 0x75, 0x60, 0xa3, 0xf0, 0x56, 0xbc, 0x1c, 0xa8, 0x59, 0x83,
0x67, 0xad, 0x6a, 0xcb, 0x6f, 0x2e, 0x03, 0x4c, 0x7f, 0x37, 0xbe, 0xeb, 0x9e, 0xd4, 0x70, 0xc4,
0x30, 0x4a, 0xf0, 0x10, 0x7f, 0x0e, 0xb9, 0x19, 0xbe, 0x36, 0xa8, 0x6f, 0x68, 0xf3, 0x7f, 0xa6,
0x1d, 0xae, 0x7a, 0xff, 0x14, 0xde, 0xcd, 0x67, 0xec, 0x31, 0x57, 0xa1, 0x14, 0x88, 0xa1, 0x4f,
0xed, 0x01, 0x42, 0x82, 0x83, 0x48, 0xf5, 0xf6, 0x08, 0xb0, 0xfe, 0x03, 0xe1, 0xf3, 0xc0, 0xaf,
0x3a, 0xcc, 0xa0, 0xce, 0x36, 0x85, 0x2e, 0xd4, 0x2e, 0x22, 0x0a, 0xe9, 0xab, 0xf8, 0xf8, 0x90,
0x6f, 0x00, 0xf1, 0xb8, 0x6b, 0xff, 0x85, 0x04, 0xc8, 0xf1, 0x6c, 0x78, 0x4f, 0xd5, 0x2d, 0x25,
0xe0, 0x13, 0xff, 0x4f, 0xda, 0x90, 0x3e, 0x9e, 0x1e, 0xb4, 0x53, 0xc1, 0x46, 0x4b, 0x11, 0x96,
0x6d, 0xb9, 0xb2, 0x8e, 0x8f, 0x26, 0xa3, 0xfc, 0x41, 0x9e, 0x6a, 0x60, 0xa4, 0x8d, 0x4c, 0x72,
0x14, 0xee, 0x9c, 0x6c, 0x6a, 0x12, 0xb6, 0x8a, 0x32, 0xca, 0xc8, 0xf6, 0x15, 0x80, 0xc6, 0x4f,
0x29, 0xcb, 0x69, 0x22, 0x40, 0x87, 0x83, 0xc6, 0xd1, 0x2e, 0x72, 0x5b, 0x01, 0x4f, 0xe4, 0x85,
0xcd, 0x17, 0xe4, 0x84, 0xc5, 0x95, 0x2b, 0xf9, 0x9b, 0xc9, 0x49, 0x41, 0xd4, 0xb1, 0x91, 0x9d,
0x04, 0x31, 0x7b, 0x8a, 0xa1, 0xbd, 0x37, 0x54, 0xec, 0xba, 0xa1, 0x0e, 0xc2, 0x27, 0xde, 0x85,
0x40, 0x69, 0x5b, 0xf2, 0xfb, 0x8e, 0xe5, 0x6f, 0x6d, 0xc5, 0x26, 0xef, 0x36, 0x66, 0x25, 0xb9,
0x1a, 0xa4, 0x97, 0x0b, 0x6f, 0xfa, 0x5c, 0x82, 0x84, 0xb9, 0xb5, 0xab, 0x85, 0x2b, 0x90, 0x5f,
0x9d, 0x83, 0xf5, 0x66, 0x9c, 0x05, 0x35, 0xbc, 0x37, 0x7b, 0xcc, 0x05, 0xad, 0x5e, 0x48, 0xe2,
0x81, 0xec, 0x0e, 0x19, 0x17, 0xca, 0x3c, 0x6a, 0x47, 0x1f, 0x8d, 0xa0, 0x89, 0x4b, 0xc8, 0x2a,
0xc2, 0xa8, 0x96, 0x54, 0x05, 0xd6, 0xee, 0xf3, 0xb5, 0xe2, 0x93, 0xa8, 0x8f, 0xda, 0x20, 0x3f,
0x09, 0xbd, 0xc7, 0x27, 0x57, 0xb1, 0x07, 0xab, 0x14, 0x88, 0x0e, 0xaa, 0x3e, 0xf7, 0x04, 0x5b,
0x58, 0x0f, 0x48, 0x21, 0xce, 0x6d, 0xd3, 0x25, 0xb5, 0xa9, 0x06, 0x55, 0xd8, 0xc5, 0xb5, 0x5f,
0x76, 0xfb, 0x84, 0x62, 0x79, 0xa9, 0xb5, 0x18, 0xc5, 0xe9, 0xb9, 0xa2, 0x11, 0x65, 0xc5, 0x09,
0x3e, 0xd4, 0x9b, 0xaa, 0xac, 0xad, 0xf1, 0xf2, 0x18, 0x73, 0x26, 0x6c, 0x76, 0x7f, 0x67, 0x69,
];
const INITIAL_PACKET_V1: &[u8] = &[
0xc0, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
0x44, 0x9e, 0x7b, 0x9a, 0xec, 0x34, 0xd1, 0xb1, 0xc9, 0x8d, 0xd7, 0x68, 0x9f, 0xb8, 0xec, 0x11,
@ -173,19 +251,19 @@ const INITIAL_PACKET_29: &[u8] = &[
0x18, 0x02, 0x77, 0x1a, 0x44, 0x9b, 0x30, 0xf3, 0xfa, 0x22, 0x89, 0x85, 0x26, 0x07, 0xb6, 0x60,
];
fn make_server(quic_version: QuicVersion) -> Connection {
fn make_server(v: Version) -> Connection {
test_fixture::fixture_init();
Connection::new_server(
test_fixture::DEFAULT_KEYS,
test_fixture::DEFAULT_ALPN,
Rc::new(RefCell::new(RandomConnectionIdGenerator::new(5))),
ConnectionParameters::default().quic_version(quic_version),
ConnectionParameters::default().versions(v, vec![v]),
)
.expect("create a default server")
}
fn process_client_initial(quic_version: QuicVersion, packet: &[u8]) {
let mut server = make_server(quic_version);
fn process_client_initial(v: Version, packet: &[u8]) {
let mut server = make_server(v);
let dgram = Datagram::new(addr(), addr(), packet);
assert_eq!(*server.state(), State::Init);
@ -194,12 +272,17 @@ fn process_client_initial(quic_version: QuicVersion, packet: &[u8]) {
assert!(out.dgram().is_some());
}
#[test]
fn process_client_initial_v2() {
process_client_initial(Version::Version2, INITIAL_PACKET_V2);
}
#[test]
fn process_client_initial_v1() {
process_client_initial(QuicVersion::Version1, INITIAL_PACKET_V1);
process_client_initial(Version::Version1, INITIAL_PACKET_V1);
}
#[test]
fn process_client_initial_29() {
process_client_initial(QuicVersion::Draft29, INITIAL_PACKET_29);
process_client_initial(Version::Draft29, INITIAL_PACKET_29);
}

Просмотреть файл

@ -7,8 +7,14 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(clippy::use_self)]
use neqo_common::Datagram;
use test_fixture::{self, default_client, default_server, now};
mod common;
use common::{
apply_header_protection, decode_initial_header, initial_aead_and_hp, remove_header_protection,
};
use neqo_common::{Datagram, Decoder, Role};
use neqo_transport::{ConnectionParameters, State, Version};
use test_fixture::{self, default_client, default_server, new_client, now, split_datagram};
#[test]
fn connect() {
@ -34,8 +40,8 @@ fn truncate_long_packet() {
dupe.destination(),
&dupe[..(dupe.len() - tail)],
);
let dupe_ack = client.process(Some(truncated), now()).dgram();
assert!(dupe_ack.is_some());
let hs_probe = client.process(Some(truncated), now()).dgram();
assert!(hs_probe.is_some());
// Now feed in the untruncated packet.
let dgram = client.process(dgram, now()).dgram();
@ -49,3 +55,73 @@ fn truncate_long_packet() {
assert!(dgram.is_some());
assert!(server.state().connected());
}
/// Test that reordering parts of the server Initial doesn't change things.
#[test]
fn reorder_server_initial() {
// A simple ACK frame for a single packet with packet number 0.
const ACK_FRAME: &[u8] = &[0x02, 0x00, 0x00, 0x00, 0x00];
let mut client = new_client(
ConnectionParameters::default().versions(Version::Version1, vec![Version::Version1]),
);
let mut server = default_server();
let client_initial = client.process_output(now()).dgram();
let (_, client_dcid, _, _) =
decode_initial_header(client_initial.as_ref().unwrap(), Role::Client);
let client_dcid = client_dcid.to_owned();
let server_packet = server.process(client_initial, now()).dgram();
let (server_initial, server_hs) = split_datagram(server_packet.as_ref().unwrap());
let (protected_header, _, _, payload) = decode_initial_header(&server_initial, Role::Server);
// Now decrypt the packet.
let (aead, hp) = initial_aead_and_hp(&client_dcid, Role::Server);
let (header, pn) = remove_header_protection(&hp, protected_header, payload);
assert_eq!(pn, 0);
let pn_len = header.len() - protected_header.len();
let mut buf = vec![0; payload.len()];
let mut plaintext = aead
.decrypt(pn, &header, &payload[pn_len..], &mut buf)
.unwrap()
.to_owned();
// Now we need to find the frames. Make some really strong assumptions.
let mut dec = Decoder::new(&plaintext[..]);
assert_eq!(dec.decode(ACK_FRAME.len()), Some(ACK_FRAME));
assert_eq!(dec.decode_varint(), Some(0x06)); // CRYPTO
assert_eq!(dec.decode_varint(), Some(0x00)); // offset
dec.skip_vvec(); // Skip over the payload.
let end = dec.offset();
// Move the ACK frame after the CRYPTO frame.
plaintext[..end].rotate_left(ACK_FRAME.len());
// And rebuild a packet.
let mut packet = header.clone();
packet.resize(1200, 0);
aead.encrypt(pn, &header, &plaintext, &mut packet[header.len()..])
.unwrap();
apply_header_protection(&hp, &mut packet, protected_header.len()..header.len());
let reordered = Datagram::new(
server_initial.source(),
server_initial.destination(),
packet,
);
// Now a connection can be made successfully.
// Though we modified the server's Initial packet, we get away with it.
// TLS only authenticates the content of the CRYPTO frame, which was untouched.
client.process_input(reordered, now());
client.process_input(server_hs.unwrap(), now());
assert!(test_fixture::maybe_authenticate(&mut client));
let finished = client.process_output(now()).dgram();
assert_eq!(*client.state(), State::Connected);
let done = server.process(finished, now()).dgram();
assert_eq!(*server.state(), State::Confirmed);
client.process_input(done.unwrap(), now());
assert_eq!(*client.state(), State::Confirmed);
}

Просмотреть файл

@ -11,10 +11,10 @@
mod common;
use common::{
apply_header_protection, client_initial_aead_and_hp, connected_server, decode_initial_header,
default_server, get_ticket, remove_header_protection,
apply_header_protection, connected_server, decode_initial_header, default_server,
generate_ticket, initial_aead_and_hp, remove_header_protection,
};
use neqo_common::{hex_with_len, qdebug, qtrace, Datagram, Encoder};
use neqo_common::{hex_with_len, qdebug, qtrace, Datagram, Encoder, Role};
use neqo_crypto::AuthenticationStatus;
use neqo_transport::{server::ValidateAddress, ConnectionError, Error, State, StreamType};
use std::convert::TryFrom;
@ -50,6 +50,26 @@ fn retry_basic() {
connected_server(&mut server);
}
/// Receiving a Retry is enough to infer something about the RTT.
/// Probably.
#[test]
fn implicit_rtt_retry() {
const RTT: Duration = Duration::from_secs(2);
let mut server = default_server();
server.set_validation(ValidateAddress::Always);
let mut client = default_client();
let mut now = now();
let dgram = client.process(None, now).dgram();
now += RTT / 2;
let dgram = server.process(dgram, now).dgram();
assertions::assert_retry(dgram.as_ref().unwrap());
now += RTT / 2;
client.process_input(dgram.unwrap(), now);
assert_eq!(client.stats().rtt, RTT);
}
#[test]
fn retry_expired() {
let mut server = default_server();
@ -76,7 +96,7 @@ fn retry_expired() {
#[test]
fn retry_0rtt() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
server.set_validation(ValidateAddress::Always);
let mut client = default_client();
@ -138,7 +158,7 @@ fn retry_different_ip() {
#[test]
fn new_token_different_ip() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
server.set_validation(ValidateAddress::NoToken);
let mut client = default_client();
@ -160,7 +180,7 @@ fn new_token_different_ip() {
#[test]
fn new_token_expired() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
server.set_validation(ValidateAddress::NoToken);
let mut client = default_client();
@ -353,10 +373,11 @@ fn mitm_retry() {
// Now to start the epic process of decrypting the packet,
// rewriting the header to remove the token, and then re-encrypting.
let client_initial2 = client_initial2.unwrap();
let (protected_header, d_cid, s_cid, payload) = decode_initial_header(&client_initial2);
let (protected_header, d_cid, s_cid, payload) =
decode_initial_header(&client_initial2, Role::Client);
// Now we have enough information to make keys.
let (aead, hp) = client_initial_aead_and_hp(d_cid);
let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client);
let (header, pn) = remove_header_protection(&hp, protected_header, payload);
let pn_len = header.len() - protected_header.len();
@ -375,12 +396,13 @@ fn mitm_retry() {
.encode_vvec(&[])
.encode_varint(u64::try_from(payload.len()).unwrap());
let pn_offset = enc.len();
let notoken_header = enc.encode_uint(pn_len, pn).to_vec();
let notoken_header = enc.encode_uint(pn_len, pn).as_ref().to_vec();
qtrace!("notoken_header={}", hex_with_len(&notoken_header));
// Encrypt.
let mut notoken_packet = Encoder::with_capacity(1200)
.encode(&notoken_header)
.as_ref()
.to_vec();
notoken_packet.resize_with(1200, u8::default);
aead.encrypt(

Просмотреть файл

@ -10,19 +10,21 @@
mod common;
use common::{
apply_header_protection, client_initial_aead_and_hp, connect, connected_server,
decode_initial_header, default_server, get_ticket, remove_header_protection,
apply_header_protection, connect, connected_server, decode_initial_header, default_server,
find_ticket, generate_ticket, initial_aead_and_hp, new_server, remove_header_protection,
};
use neqo_common::{qtrace, Datagram, Decoder, Encoder};
use neqo_crypto::{generate_ech_keys, AllowZeroRtt, ZeroRttCheckResult, ZeroRttChecker};
use neqo_common::{qtrace, Datagram, Decoder, Encoder, Role};
use neqo_crypto::{
generate_ech_keys, AllowZeroRtt, AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker,
};
use neqo_transport::{
server::{ActiveConnectionRef, Server, ValidateAddress},
Connection, ConnectionError, ConnectionParameters, Error, Output, QuicVersion, State,
StreamType,
Connection, ConnectionError, ConnectionParameters, Error, Output, State, StreamType, Version,
};
use test_fixture::{
self, assertions, default_client, now, split_datagram, CountingConnectionIdGenerator,
self, assertions, default_client, new_client, now, split_datagram,
CountingConnectionIdGenerator,
};
use std::cell::RefCell;
@ -66,6 +68,70 @@ fn single_client() {
connect(&mut client, &mut server);
}
#[test]
fn connect_single_version_both() {
fn connect_one_version(version: Version) {
let params = ConnectionParameters::default().versions(version, vec![version]);
let mut server = new_server(params.clone());
let mut client = new_client(params);
let server_conn = connect(&mut client, &mut server);
assert_eq!(client.version(), version);
assert_eq!(server_conn.borrow().version(), version);
}
for v in Version::all() {
println!("Connecting with {:?}", v);
connect_one_version(v);
}
}
#[test]
fn connect_single_version_client() {
fn connect_one_version(version: Version) {
let mut server = default_server();
let mut client =
new_client(ConnectionParameters::default().versions(version, vec![version]));
let server_conn = connect(&mut client, &mut server);
assert_eq!(client.version(), version);
assert_eq!(server_conn.borrow().version(), version);
}
for v in Version::all() {
println!("Connecting with {:?}", v);
connect_one_version(v);
}
}
#[test]
fn connect_single_version_server() {
fn connect_one_version(version: Version) {
let mut server =
new_server(ConnectionParameters::default().versions(version, vec![version]));
let mut client = default_client();
if client.version() != version {
// Run the version negotiation exchange if necessary.
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
let dgram = server.process(dgram, now()).dgram();
assertions::assert_vn(dgram.as_ref().unwrap());
client.process_input(dgram.unwrap(), now());
}
let server_conn = connect(&mut client, &mut server);
assert_eq!(client.version(), version);
assert_eq!(server_conn.borrow().version(), version);
}
for v in Version::all() {
println!("Connecting with {:?}", v);
connect_one_version(v);
}
}
#[test]
fn duplicate_initial() {
let mut server = default_server();
@ -164,7 +230,7 @@ fn drop_non_initial() {
let mut header = neqo_common::Encoder::with_capacity(1200);
header
.encode_byte(0xfa)
.encode_uint(4, QuicVersion::default().as_u32())
.encode_uint(4, Version::default().wire_version())
.encode_vec(1, CID)
.encode_vec(1, CID);
let mut bogus_data: Vec<u8> = header.into();
@ -183,7 +249,7 @@ fn drop_short_initial() {
let mut header = neqo_common::Encoder::with_capacity(1199);
header
.encode_byte(0xca)
.encode_uint(4, QuicVersion::default().as_u32())
.encode_uint(4, Version::default().wire_version())
.encode_vec(1, CID)
.encode_vec(1, CID);
let mut bogus_data: Vec<u8> = header.into();
@ -200,7 +266,7 @@ fn drop_short_initial() {
#[test]
fn zero_rtt() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
// Discharge the old connection so that we don't have to worry about it.
let mut now = now();
@ -262,7 +328,7 @@ fn zero_rtt() {
#[test]
fn new_token_0rtt() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
server.set_validation(ValidateAddress::NoToken);
let mut client = default_client();
@ -293,7 +359,7 @@ fn new_token_0rtt() {
#[test]
fn new_token_different_port() {
let mut server = default_server();
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
server.set_validation(ValidateAddress::NoToken);
let mut client = default_client();
@ -318,8 +384,8 @@ fn bad_client_initial() {
let mut server = default_server();
let dgram = client.process(None, now()).dgram().expect("a datagram");
let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram);
let (aead, hp) = client_initial_aead_and_hp(d_cid);
let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram, Role::Client);
let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client);
let (fixed_header, pn) = remove_header_protection(&hp, header, payload);
let payload = &payload[(fixed_header.len() - header.len())..];
@ -335,20 +401,20 @@ fn bad_client_initial() {
let mut header_enc = Encoder::new();
header_enc
.encode_byte(0xc0) // Initial with 1 byte packet number.
.encode_uint(4, QuicVersion::default().as_u32())
.encode_uint(4, Version::default().wire_version())
.encode_vec(1, d_cid)
.encode_vec(1, s_cid)
.encode_vvec(&[])
.encode_varint(u64::try_from(payload_enc.len() + aead.expansion() + 1).unwrap())
.encode_byte(u8::try_from(pn).unwrap());
let mut ciphertext = header_enc.to_vec();
let mut ciphertext = header_enc.as_ref().to_vec();
ciphertext.resize(header_enc.len() + payload_enc.len() + aead.expansion(), 0);
let v = aead
.encrypt(
pn,
&header_enc,
&payload_enc,
header_enc.as_ref(),
payload_enc.as_ref(),
&mut ciphertext[header_enc.len()..],
)
.unwrap();
@ -401,7 +467,7 @@ fn bad_client_initial() {
}
#[test]
fn version_negotiation() {
fn version_negotiation_ignored() {
let mut server = default_server();
let mut client = default_client();
@ -426,7 +492,7 @@ fn version_negotiation() {
let mut found = false;
while dec.remaining() > 0 {
let v = dec.decode_uint(4).expect("supported version");
found |= v == u64::from(QuicVersion::default().as_u32());
found |= v == u64::from(Version::default().wire_version());
}
assert!(found, "valid version not found");
@ -436,6 +502,128 @@ fn version_negotiation() {
assert_eq!(client.state(), &State::WaitInitial);
}
/// Test that if the server doesn't support a version it will signal with a
/// Version Negotiation packet and the client will use that version.
#[test]
fn version_negotiation() {
const VN_VERSION: Version = Version::Draft29;
assert_ne!(VN_VERSION, Version::default());
assert!(!Version::default().is_compatible(VN_VERSION));
let mut server =
new_server(ConnectionParameters::default().versions(VN_VERSION, vec![VN_VERSION]));
let mut client = default_client();
// `connect()` runs a fixed exchange, so manually run the Version Negotiation.
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
let dgram = server.process(dgram, now()).dgram();
assertions::assert_vn(dgram.as_ref().unwrap());
client.process_input(dgram.unwrap(), now());
let sconn = connect(&mut client, &mut server);
assert_eq!(client.version(), VN_VERSION);
assert_eq!(sconn.borrow().version(), VN_VERSION);
}
/// Test that the client can pick a version from a Version Negotiation packet,
/// which is then subsequently upgraded to a compatible version by the server.
#[test]
fn version_negotiation_and_compatible() {
const ORIG_VERSION: Version = Version::Draft29;
const VN_VERSION: Version = Version::Version1;
const COMPAT_VERSION: Version = Version::Version2;
assert!(!ORIG_VERSION.is_compatible(VN_VERSION));
assert!(!ORIG_VERSION.is_compatible(COMPAT_VERSION));
assert!(VN_VERSION.is_compatible(COMPAT_VERSION));
let mut server = new_server(
ConnectionParameters::default().versions(VN_VERSION, vec![COMPAT_VERSION, VN_VERSION]),
);
// Note that the order of versions at the client only determines what it tries first.
// The server will pick between VN_VERSION and COMPAT_VERSION.
let mut client = new_client(
ConnectionParameters::default()
.versions(ORIG_VERSION, vec![ORIG_VERSION, VN_VERSION, COMPAT_VERSION]),
);
// Run the full exchange so that we can observe the versions in use.
// Version Negotiation
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
assertions::assert_version(dgram.as_ref().unwrap(), ORIG_VERSION.wire_version());
let dgram = server.process(dgram, now()).dgram();
assertions::assert_vn(dgram.as_ref().unwrap());
client.process_input(dgram.unwrap(), now());
let dgram = client.process(None, now()).dgram(); // ClientHello
assertions::assert_version(dgram.as_ref().unwrap(), VN_VERSION.wire_version());
let dgram = server.process(dgram, now()).dgram(); // ServerHello...
assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version());
client.process_input(dgram.unwrap(), now());
client.authenticated(AuthenticationStatus::Ok, now());
let dgram = client.process_output(now()).dgram();
assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version());
assert_eq!(*client.state(), State::Connected);
let dgram = server.process(dgram, now()).dgram(); // ACK + HANDSHAKE_DONE + NST
client.process_input(dgram.unwrap(), now());
assert_eq!(*client.state(), State::Confirmed);
let sconn = connected_server(&mut server);
assert_eq!(client.version(), COMPAT_VERSION);
assert_eq!(sconn.borrow().version(), COMPAT_VERSION);
}
/// When a client resumes it remembers the version that the connection last used.
/// A subsequent connection will use that version, but if it then receives
/// a version negotiation packet, it should validate based on what it attempted
/// not what it was originally configured for.
#[test]
fn compatible_upgrade_resumption_and_vn() {
// Start at v1, compatible upgrade to v2.
const ORIG_VERSION: Version = Version::Version1;
const COMPAT_VERSION: Version = Version::Version2;
const RESUMPTION_VERSION: Version = Version::Draft29;
let client_params = ConnectionParameters::default().versions(
ORIG_VERSION,
vec![COMPAT_VERSION, ORIG_VERSION, RESUMPTION_VERSION],
);
let mut client = new_client(client_params.clone());
assert_eq!(client.version(), ORIG_VERSION);
let mut server = default_server();
let mut server_conn = connect(&mut client, &mut server);
assert_eq!(client.version(), COMPAT_VERSION);
assert_eq!(server_conn.borrow().version(), COMPAT_VERSION);
server_conn.borrow_mut().send_ticket(now(), &[]).unwrap();
let dgram = server.process(None, now()).dgram();
client.process_input(dgram.unwrap(), now()); // Consume ticket, ignore output.
let ticket = find_ticket(&mut client);
// This new server will reject the ticket, but it will also generate a VN packet.
let mut client = new_client(client_params);
let mut server = new_server(
ConnectionParameters::default().versions(RESUMPTION_VERSION, vec![RESUMPTION_VERSION]),
);
client.enable_resumption(now(), ticket).unwrap();
// The version negotiation exchange.
let dgram = client.process_output(now()).dgram();
assert!(dgram.is_some());
assertions::assert_version(dgram.as_ref().unwrap(), COMPAT_VERSION.wire_version());
let dgram = server.process(dgram, now()).dgram();
assertions::assert_vn(dgram.as_ref().unwrap());
client.process_input(dgram.unwrap(), now());
let server_conn = connect(&mut client, &mut server);
assert_eq!(client.version(), RESUMPTION_VERSION);
assert_eq!(server_conn.borrow().version(), RESUMPTION_VERSION);
}
#[test]
fn closed() {
// Let a server connection idle and it should be removed.
@ -533,7 +721,7 @@ fn max_streams_after_0rtt_rejection() {
.max_streams(StreamType::UniDi, MAX_STREAMS_UNIDI),
)
.expect("should create a server");
let token = get_ticket(&mut server);
let token = generate_ticket(&mut server);
let mut client = default_client();
client.enable_resumption(now(), &token).unwrap();

Просмотреть файл

@ -59,7 +59,7 @@ macro_rules! simulate {
#[test]
fn $n() {
let fixture = $setup;
let mut nodes: Vec<Box<dyn crate::sim::Node>> = Vec::new();
let mut nodes: Vec<Box<dyn $crate::sim::Node>> = Vec::new();
$(
let f: Box<dyn FnOnce(&_) -> _> = Box::new($v);
nodes.push(Box::new(f(&fixture)));
@ -149,7 +149,7 @@ impl Simulator {
/// Though this is convenient, it panics if this isn't a 64 character hex string.
pub fn seed_str(&mut self, seed: impl AsRef<str>) {
let seed = Encoder::from_hex(seed);
self.seed(<[u8; 32]>::try_from(&seed[..]).unwrap());
self.seed(<[u8; 32]>::try_from(seed.as_ref()).unwrap());
}
fn next_time(&self, now: Instant) -> Instant {