Skip to content

Commit

Permalink
proto: Refactor TokenDecodeError
Browse files Browse the repository at this point in the history
  • Loading branch information
gretchenfrage committed Dec 15, 2024
1 parent 73545b6 commit f53f8f2
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 27 deletions.
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::Ignore) => (None, header.dst_cid),
_ => {
debug!("rejecting invalid stateless retry token");
return Some(DatagramEvent::Response(self.initial_close(
Expand Down
51 changes: 31 additions & 20 deletions quinn-proto/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,19 @@ 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::Ignore)?;
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)?;
let orig_dst_cid = ConnectionId::decode_long(&mut reader).ok_or(ValidationError::Ignore)?;
let issued = UNIX_EPOCH
+ Duration::new(
reader
.get::<u64>()
.map_err(|_| TokenDecodeError::UnknownToken)?,
0,
);
+ Duration::new(reader.get::<u64>().map_err(|_| ValidationError::Ignore)?, 0);

Ok(Self {
orig_dst_cid,
Expand Down Expand Up @@ -99,19 +93,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.
Ignore,
/// 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::Ignore
}
}

Expand Down

0 comments on commit f53f8f2

Please sign in to comment.