fix: remove quinn-plaintext (#2345)

Remove quinn-plaintext to fix connection errors caused
by different hash values ​​across platforms.

On x64, maintain compatibility with quinn-plaintext.
This commit is contained in:
Luna Yao
2026-06-13 19:21:35 +02:00
committed by GitHub
parent e7709f1cb5
commit 5edc4cb1cd
3 changed files with 251 additions and 17 deletions
Generated
+2 -13
View File
@@ -2318,7 +2318,7 @@ dependencies = [
"prost-reflect-build", "prost-reflect-build",
"prost-wkt-types", "prost-wkt-types",
"quinn", "quinn",
"quinn-plaintext", "quinn-proto",
"quote", "quote",
"rand 0.8.5", "rand 0.8.5",
"rcgen", "rcgen",
@@ -2330,6 +2330,7 @@ dependencies = [
"rstest", "rstest",
"rust-i18n", "rust-i18n",
"rustls", "rustls",
"seahash",
"serde", "serde",
"serde_json", "serde_json",
"serial_test", "serial_test",
@@ -7022,18 +7023,6 @@ dependencies = [
"web-time", "web-time",
] ]
[[package]]
name = "quinn-plaintext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3e617feaeb6493018fa35fc47ae8b630ac8903d8159e9e747018841b99bad3d"
dependencies = [
"bytes",
"quinn-proto",
"seahash",
"tracing",
]
[[package]] [[package]]
name = "quinn-proto" name = "quinn-proto"
version = "0.11.12" version = "0.11.12"
+3 -2
View File
@@ -82,7 +82,8 @@ pin-project-lite = "0.2.13"
atomic_refcell = "0.1.13" atomic_refcell = "0.1.13"
quinn = { version = "0.11.8", optional = true, features = ["ring"] } quinn = { version = "0.11.8", optional = true, features = ["ring"] }
quinn-plaintext = { version = "0.3.0", optional = true } quinn-proto = { version = "0.11.12", optional = true }
seahash = { version = "4.1.0", optional = true }
rustls = { version = "0.23.0", features = [ rustls = { version = "0.23.0", features = [
"ring", "tls12" "ring", "tls12"
@@ -373,7 +374,7 @@ full = [
"zstd", "zstd",
] ]
wireguard = ["dep:boringtun", "dep:ring"] wireguard = ["dep:boringtun", "dep:ring"]
quic = ["dep:quinn", "dep:quinn-plaintext", "dep:rustls", "dep:rcgen"] quic = ["dep:quinn", "dep:quinn-proto", "dep:seahash", "dep:rustls", "dep:rcgen"]
kcp = ["dep:kcp-sys"] kcp = ["dep:kcp-sys"]
mimalloc = ["dep:mimalloc"] mimalloc = ["dep:mimalloc"]
aes-gcm = ["dep:aes-gcm"] aes-gcm = ["dep:aes-gcm"]
+246 -2
View File
@@ -23,6 +23,250 @@ use std::{net::SocketAddr, sync::Arc, time::Duration};
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
// region config // region config
mod crypto {
use crate::utils::BoxExt;
use bytes::{Buf, BytesMut};
use quinn_proto::crypto::{
ClientConfig, ExportKeyingMaterialError, KeyPair, Keys, ServerConfig, Session,
UnsupportedVersion,
};
use quinn_proto::transport_parameters::TransportParameters;
use quinn_proto::{
ConnectError, ConnectionId, Side, TransportError,
crypto::{CryptoError, HeaderKey, PacketKey},
};
use seahash::SeaHasher;
use std::any::Any;
use std::{hash::Hasher, sync::Arc};
use tracing::{error, instrument, trace};
#[derive(Debug, Clone, Copy)]
struct CryptoKey;
impl CryptoKey {
fn header(self) -> KeyPair<Box<dyn HeaderKey>> {
KeyPair {
local: Box::new(self),
remote: Box::new(self),
}
}
fn packet(self) -> KeyPair<Box<dyn PacketKey>> {
KeyPair {
local: Box::new(self),
remote: Box::new(self),
}
}
fn keys(self) -> Keys {
Keys {
header: self.header(),
packet: self.packet(),
}
}
}
impl HeaderKey for CryptoKey {
fn decrypt(&self, _: usize, _: &mut [u8]) {}
fn encrypt(&self, _: usize, _: &mut [u8]) {}
fn sample_size(&self) -> usize {
0
}
}
impl CryptoKey {
fn checksum(slices: &[&[u8]]) -> u64 {
let mut hasher = SeaHasher::default();
for slice in slices {
hasher.write(&(slice.len() as u64).to_le_bytes());
hasher.write(slice);
}
hasher.finish()
}
}
impl PacketKey for CryptoKey {
#[instrument(level = "trace")]
fn encrypt(&self, packet: u64, buf: &mut [u8], header_len: usize) {
let (header, rest) = buf.split_at_mut(header_len);
let (payload, tag) = rest.split_at_mut(rest.len() - self.tag_len());
let checksum = Self::checksum(&[header, payload]);
tag.copy_from_slice(&checksum.to_be_bytes());
trace!(checksum, ?header, ?payload, ?tag);
}
#[instrument(level = "trace")]
fn decrypt(
&self,
packet: u64,
header: &[u8],
payload: &mut BytesMut,
) -> Result<(), CryptoError> {
let tag = payload.split_off(payload.len() - self.tag_len()).get_u64();
trace!(tag, ?payload);
let checksum = Self::checksum(&[header, payload]);
if checksum != tag {
error!(tag, checksum, "checksum mismatch");
return Err(CryptoError);
}
Ok(())
}
fn tag_len(&self) -> usize {
8
}
fn confidentiality_limit(&self) -> u64 {
u64::MAX
}
fn integrity_limit(&self) -> u64 {
1 << 36
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HandshakeState {
EmitInitial,
EmitHandshake,
Done,
}
#[derive(Debug)]
struct QuicSession {
side: Side,
state: HandshakeState,
local: TransportParameters,
remote: Option<TransportParameters>,
}
impl QuicSession {
fn new(side: Side, params: TransportParameters) -> Self {
Self {
side,
state: HandshakeState::EmitInitial,
local: params,
remote: None,
}
}
}
impl Session for QuicSession {
fn initial_keys(&self, _: &ConnectionId, _: Side) -> Keys {
CryptoKey.keys()
}
fn handshake_data(&self) -> Option<Box<dyn Any>> {
self.remote.map(|params| params.boxed() as _)
}
fn peer_identity(&self) -> Option<Box<dyn Any>> {
None
}
fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)> {
None
}
fn early_data_accepted(&self) -> Option<bool> {
Some(false)
}
#[instrument(level = "trace")]
fn is_handshaking(&self) -> bool {
self.remote.is_none() || self.state != HandshakeState::Done
}
#[instrument(level = "trace")]
fn read_handshake(&mut self, mut buf: &[u8]) -> Result<bool, TransportError> {
if self.remote.is_none() {
self.remote = Some(
TransportParameters::read(self.side, &mut buf)
.expect("failed to read transport parameters"),
);
}
Ok(true)
}
#[instrument(level = "trace")]
fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
Ok(self.remote)
}
#[instrument(level = "trace")]
fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
match self.state {
HandshakeState::EmitInitial => {
if self.side.is_client() {
self.local.write(buf);
}
self.state = HandshakeState::EmitHandshake;
Some(CryptoKey.keys())
}
HandshakeState::EmitHandshake => {
if self.side.is_server() {
self.local.write(buf);
}
self.state = HandshakeState::Done;
Some(CryptoKey.keys())
}
HandshakeState::Done => None,
}
}
fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>> {
Some(CryptoKey.packet())
}
fn is_valid_retry(&self, _: &ConnectionId, _: &[u8], _: &[u8]) -> bool {
true
}
fn export_keying_material(
&self,
_: &mut [u8],
_: &[u8],
_: &[u8],
) -> Result<(), ExportKeyingMaterialError> {
Ok(())
}
}
#[derive(Debug)]
pub struct CryptoConfig;
impl ClientConfig for CryptoConfig {
#[instrument(level = "trace")]
fn start_session(
self: Arc<Self>,
version: u32,
server_name: &str,
params: &TransportParameters,
) -> Result<Box<dyn Session>, ConnectError> {
Ok(Box::new(QuicSession::new(Side::Client, *params)))
}
}
impl ServerConfig for CryptoConfig {
fn initial_keys(&self, _: u32, _: &ConnectionId) -> Result<Keys, UnsupportedVersion> {
Ok(CryptoKey.keys())
}
fn retry_tag(&self, _: u32, _: &ConnectionId, _: &[u8]) -> [u8; 16] {
[0u8; 16]
}
#[instrument(level = "trace")]
fn start_session(
self: Arc<Self>,
version: u32,
params: &TransportParameters,
) -> Box<dyn Session> {
Box::new(QuicSession::new(Side::Server, *params))
}
}
}
pub fn transport_config() -> Arc<TransportConfig> { pub fn transport_config() -> Arc<TransportConfig> {
let mut config = TransportConfig::default(); let mut config = TransportConfig::default();
@@ -39,13 +283,13 @@ pub fn transport_config() -> Arc<TransportConfig> {
} }
pub fn server_config() -> ServerConfig { pub fn server_config() -> ServerConfig {
let mut config = quinn_plaintext::server_config(); let mut config = ServerConfig::with_crypto(Arc::new(crypto::CryptoConfig));
config.transport_config(transport_config()); config.transport_config(transport_config());
config config
} }
pub fn client_config() -> ClientConfig { pub fn client_config() -> ClientConfig {
let mut config = quinn_plaintext::client_config(); let mut config = ClientConfig::new(Arc::new(crypto::CryptoConfig));
config.transport_config(transport_config()); config.transport_config(transport_config());
config config
} }