quinn_proto/
token.rs
1use std::{
2 fmt, io,
3 net::{IpAddr, SocketAddr},
4 time::{Duration, SystemTime, UNIX_EPOCH},
5};
6
7use bytes::{Buf, BufMut};
8
9use crate::{
10 coding::{BufExt, BufMutExt},
11 crypto::{CryptoError, HandshakeTokenKey, HmacKey},
12 shared::ConnectionId,
13 RESET_TOKEN_SIZE,
14};
15
16pub(crate) struct RetryToken {
17 pub(crate) orig_dst_cid: ConnectionId,
19 pub(crate) issued: SystemTime,
21}
22
23impl RetryToken {
24 pub(crate) fn encode(
25 &self,
26 key: &dyn HandshakeTokenKey,
27 address: &SocketAddr,
28 retry_src_cid: &ConnectionId,
29 ) -> Vec<u8> {
30 let aead_key = key.aead_from_hkdf(retry_src_cid);
31
32 let mut buf = Vec::new();
33 encode_addr(&mut buf, address);
34 self.orig_dst_cid.encode_long(&mut buf);
35 buf.write::<u64>(
36 self.issued
37 .duration_since(UNIX_EPOCH)
38 .map(|x| x.as_secs())
39 .unwrap_or(0),
40 );
41
42 aead_key.seal(&mut buf, &[]).unwrap();
43
44 buf
45 }
46
47 pub(crate) fn from_bytes(
48 key: &dyn HandshakeTokenKey,
49 address: &SocketAddr,
50 retry_src_cid: &ConnectionId,
51 raw_token_bytes: &[u8],
52 ) -> Result<Self, TokenDecodeError> {
53 let aead_key = key.aead_from_hkdf(retry_src_cid);
54 let mut sealed_token = raw_token_bytes.to_vec();
55
56 let data = aead_key.open(&mut sealed_token, &[])?;
57 let mut reader = io::Cursor::new(data);
58 let token_addr = decode_addr(&mut reader).ok_or(TokenDecodeError::UnknownToken)?;
59 if token_addr != *address {
60 return Err(TokenDecodeError::WrongAddress);
61 }
62 let orig_dst_cid =
63 ConnectionId::decode_long(&mut reader).ok_or(TokenDecodeError::UnknownToken)?;
64 let issued = UNIX_EPOCH
65 + Duration::new(
66 reader
67 .get::<u64>()
68 .map_err(|_| TokenDecodeError::UnknownToken)?,
69 0,
70 );
71
72 Ok(Self {
73 orig_dst_cid,
74 issued,
75 })
76 }
77}
78
79fn encode_addr(buf: &mut Vec<u8>, address: &SocketAddr) {
80 match address.ip() {
81 IpAddr::V4(x) => {
82 buf.put_u8(0);
83 buf.put_slice(&x.octets());
84 }
85 IpAddr::V6(x) => {
86 buf.put_u8(1);
87 buf.put_slice(&x.octets());
88 }
89 }
90 buf.put_u16(address.port());
91}
92
93fn decode_addr<B: Buf>(buf: &mut B) -> Option<SocketAddr> {
94 let ip = match buf.get_u8() {
95 0 => IpAddr::V4(buf.get().ok()?),
96 1 => IpAddr::V6(buf.get().ok()?),
97 _ => return None,
98 };
99 let port = buf.get_u16();
100 Some(SocketAddr::new(ip, port))
101}
102
103#[derive(Debug, Copy, Clone)]
105pub(crate) enum TokenDecodeError {
106 UnknownToken,
108 WrongAddress,
111}
112
113impl From<CryptoError> for TokenDecodeError {
114 fn from(CryptoError: CryptoError) -> Self {
115 Self::UnknownToken
116 }
117}
118
119#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, Copy, Clone, Hash)]
124pub(crate) struct ResetToken([u8; RESET_TOKEN_SIZE]);
125
126impl ResetToken {
127 pub(crate) fn new(key: &dyn HmacKey, id: &ConnectionId) -> Self {
128 let mut signature = vec![0; key.signature_len()];
129 key.sign(id, &mut signature);
130 let mut result = [0; RESET_TOKEN_SIZE];
132 result.copy_from_slice(&signature[..RESET_TOKEN_SIZE]);
133 result.into()
134 }
135}
136
137impl PartialEq for ResetToken {
138 fn eq(&self, other: &Self) -> bool {
139 crate::constant_time::eq(&self.0, &other.0)
140 }
141}
142
143impl Eq for ResetToken {}
144
145impl From<[u8; RESET_TOKEN_SIZE]> for ResetToken {
146 fn from(x: [u8; RESET_TOKEN_SIZE]) -> Self {
147 Self(x)
148 }
149}
150
151impl std::ops::Deref for ResetToken {
152 type Target = [u8];
153 fn deref(&self) -> &[u8] {
154 &self.0
155 }
156}
157
158impl fmt::Display for ResetToken {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 for byte in self.iter() {
161 write!(f, "{byte:02x}")?;
162 }
163 Ok(())
164 }
165}
166
167#[cfg(test)]
168mod test {
169 #[cfg(feature = "ring")]
170 #[test]
171 fn token_sanity() {
172 use super::*;
173 use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
174 use crate::MAX_CID_SIZE;
175
176 use rand::RngCore;
177 use std::{
178 net::Ipv6Addr,
179 time::{Duration, UNIX_EPOCH},
180 };
181
182 let rng = &mut rand::thread_rng();
183
184 let mut master_key = [0; 64];
185 rng.fill_bytes(&mut master_key);
186
187 let prk = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, &[]).extract(&master_key);
188
189 let addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
190 let retry_src_cid = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
191 let token = RetryToken {
192 orig_dst_cid: RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid(),
193 issued: UNIX_EPOCH + Duration::new(42, 0), };
195 let encoded = token.encode(&prk, &addr, &retry_src_cid);
196
197 let decoded = RetryToken::from_bytes(&prk, &addr, &retry_src_cid, &encoded)
198 .expect("token didn't validate");
199 assert_eq!(token.orig_dst_cid, decoded.orig_dst_cid);
200 assert_eq!(token.issued, decoded.issued);
201 }
202
203 #[cfg(feature = "ring")]
204 #[test]
205 fn invalid_token_returns_err() {
206 use super::*;
207 use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
208 use crate::MAX_CID_SIZE;
209 use rand::RngCore;
210 use std::net::Ipv6Addr;
211
212 let rng = &mut rand::thread_rng();
213
214 let mut master_key = [0; 64];
215 rng.fill_bytes(&mut master_key);
216
217 let prk = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, &[]).extract(&master_key);
218
219 let addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
220 let retry_src_cid = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
221
222 let mut invalid_token = Vec::new();
223
224 let mut random_data = [0; 32];
225 rand::thread_rng().fill_bytes(&mut random_data);
226 invalid_token.put_slice(&random_data);
227
228 assert!(RetryToken::from_bytes(&prk, &addr, &retry_src_cid, &invalid_token).is_err());
230 }
231}