Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proto: Refactor TokenDecodeError #2085

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions quinn-proto/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
EndpointEvent, EndpointEventInner, IssuedCid,
},
token::TokenDecodeError,
token,
transport_parameters::{PreferredAddress, TransportParameters},
Duration, Instant, ResetToken, RetryToken, Side, SystemTime, Transmit, TransportConfig,
TransportError, INITIAL_MTU, MAX_CID_SIZE, MIN_INITIAL_SIZE, RESET_TOKEN_SIZE,
Expand Down Expand Up @@ -510,12 +510,7 @@ impl Endpoint {
{
(Some(header.dst_cid), token.orig_dst_cid)
}
Err(TokenDecodeError::UnknownToken) => {
// Token may have been generated by an incompatible endpoint, e.g. a
// different version or a neighbor behind the same load balancer. We
// can't interpret it, so we proceed as if there was no token.
(None, header.dst_cid)
}
Err(token::ValidationError::Unusable) => (None, header.dst_cid),
_ => {
debug!("rejecting invalid stateless retry token");
return Some(DatagramEvent::Response(self.initial_close(
Expand Down
47 changes: 31 additions & 16 deletions quinn-proto/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,21 @@ impl RetryToken {
address: &SocketAddr,
retry_src_cid: &ConnectionId,
raw_token_bytes: &[u8],
) -> Result<Self, TokenDecodeError> {
) -> Result<Self, ValidationError> {
let aead_key = key.aead_from_hkdf(retry_src_cid);
let mut sealed_token = raw_token_bytes.to_vec();

let data = aead_key.open(&mut sealed_token, &[])?;
let mut reader = io::Cursor::new(data);
let token_addr = decode_addr(&mut reader).ok_or(TokenDecodeError::UnknownToken)?;
let token_addr = decode_addr(&mut reader).ok_or(ValidationError::Unusable)?;
if token_addr != *address {
return Err(TokenDecodeError::WrongAddress);
return Err(ValidationError::InvalidRetry);
}
let orig_dst_cid =
ConnectionId::decode_long(&mut reader).ok_or(TokenDecodeError::UnknownToken)?;
ConnectionId::decode_long(&mut reader).ok_or(ValidationError::Unusable)?;
let issued = UNIX_EPOCH
+ Duration::new(
reader
.get::<u64>()
.map_err(|_| TokenDecodeError::UnknownToken)?,
reader.get::<u64>().map_err(|_| ValidationError::Unusable)?,
0,
);

Expand Down Expand Up @@ -99,19 +97,36 @@ fn decode_addr<B: Buf>(buf: &mut B) -> Option<SocketAddr> {
Some(SocketAddr::new(ip, port))
}

/// Reasons why a retry token might fail to validate a client's address
/// Error for a token failing to validate a client's address
#[derive(Debug, Copy, Clone)]
pub(crate) enum TokenDecodeError {
/// Token was not recognized. It should be silently ignored.
UnknownToken,
/// Token was well-formed but associated with an incorrect address. The connection cannot be
/// established.
WrongAddress,
pub(crate) enum ValidationError {
/// Token may have come from a NEW_TOKEN frame (including from a different server or a previous
/// run of this server with different keys), and was not valid
///
/// It should be silently ignored.
///
/// In cases where a token cannot be decrypted/decoded, we must allow for the possibility that
/// this is caused not by client malfeasance, but by the token having been generated by an
/// incompatible endpoint, e.g. a different version or a neighbor behind the same load
/// balancer. In such cases we proceed as if there was no token.
///
/// [_RFC 9000 § 8.1.3:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.3-10)
///
/// > If the token is invalid, then the server SHOULD proceed as if the client did not have a
/// > validated address, including potentially sending a Retry packet.
///
/// That said, this may also be used when a token _can_ be unambiguously decrypted/decoded as a
/// token from a NEW_TOKEN frame, but is simply not valid.
Unusable,
/// Token was unambiguously from a Retry packet, and was not valid
///
/// The connection cannot be established.
InvalidRetry,
}

impl From<CryptoError> for TokenDecodeError {
impl From<CryptoError> for ValidationError {
fn from(CryptoError: CryptoError) -> Self {
Self::UnknownToken
Self::Unusable
}
}

Expand Down
Loading