1#![allow(missing_docs)]
15
16use rand::RngCore;
20
21use crate::{nat_traversal_api::PeerId, shared::ConnectionId};
22
23#[cfg(feature = "ring")]
25use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM, NONCE_LEN};
26
27#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
28use aws_lc_rs::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM, NONCE_LEN};
29
30#[derive(Clone)]
33pub struct TokenKey(pub [u8; 32]);
34
35#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct RetryTokenDecoded {
39 pub peer_id: PeerId,
41 pub cid: ConnectionId,
43 pub nonce: u128,
45}
46
47pub fn test_key_from_rng(rng: &mut dyn RngCore) -> TokenKey {
50 let mut k = [0u8; 32];
51 rng.fill_bytes(&mut k);
52 TokenKey(k)
53}
54
55pub fn encode_retry_token_with_rng<R: RngCore>(
59 key: &TokenKey,
60 peer_id: &PeerId,
61 cid: &ConnectionId,
62 rng: &mut R,
63) -> Vec<u8> {
64 let mut nonce_bytes = [0u8; 12]; rng.fill_bytes(&mut nonce_bytes);
66
67 let mut pt = Vec::with_capacity(32 + 1 + crate::MAX_CID_SIZE + 12);
68 pt.extend_from_slice(&peer_id.0);
69 pt.push(cid.len() as u8);
70 pt.extend_from_slice(&cid[..]);
71 pt.extend_from_slice(&nonce_bytes); seal(&key.0, &nonce_bytes, &pt)
73}
74
75pub fn encode_retry_token(key: &TokenKey, peer_id: &PeerId, cid: &ConnectionId) -> Vec<u8> {
76 encode_retry_token_with_rng(key, peer_id, cid, &mut rand::thread_rng())
77}
78
79pub fn decode_retry_token(key: &TokenKey, token: &[u8]) -> Option<RetryTokenDecoded> {
83 let (ct, nonce_suffix) = token.split_at(token.len().checked_sub(12)?);
85 let mut nonce12 = [0u8; 12];
86 nonce12.copy_from_slice(nonce_suffix);
87 let plaintext = open(&key.0, &nonce12, ct).ok()?;
88 if plaintext.len() < 32 + 1 + 12 {
89 return None;
90 } let mut off = 0usize;
92 let mut pid = [0u8; 32];
93 pid.copy_from_slice(&plaintext[off..off + 32]);
94 off += 32;
95 let cid_len = plaintext[off] as usize;
96 off += 1;
97 if plaintext.len() < off + cid_len + 12 {
98 return None;
99 }
100 let mut cid_buf = [0u8; crate::MAX_CID_SIZE];
101 cid_buf[..cid_len].copy_from_slice(&plaintext[off..off + cid_len]);
102 let cid = ConnectionId::new(&cid_buf[..cid_len]);
103 off += cid_len;
104 let mut nonce_arr = [0u8; 12];
105 nonce_arr.copy_from_slice(&plaintext[off..off + 12]);
106 let mut nonce_bytes_16 = [0u8; 16];
107 nonce_bytes_16[..12].copy_from_slice(&nonce_arr);
108 let nonce = u128::from_le_bytes(nonce_bytes_16); Some(RetryTokenDecoded {
110 peer_id: PeerId(pid),
111 cid,
112 nonce,
113 })
114}
115
116pub fn validate_token(
119 key: &TokenKey,
120 token: &[u8],
121 expected_peer: &PeerId,
122 expected_cid: &ConnectionId,
123) -> bool {
124 match decode_retry_token(key, token) {
125 Some(dec) => dec.peer_id == *expected_peer && dec.cid == *expected_cid,
126 None => false,
127 }
128}
129
130#[allow(clippy::expect_used, clippy::let_unit_value)]
133fn seal(key: &[u8; 32], nonce: &[u8; 12], pt: &[u8]) -> Vec<u8> {
134 let unbound_key = UnboundKey::new(&AES_256_GCM, key).expect("invalid key length");
135 let key = LessSafeKey::new(unbound_key);
136
137 let nonce_bytes = *nonce;
139
140 let nonce = Nonce::try_assume_unique_for_key(&nonce_bytes).expect("invalid nonce length");
142
143 let mut in_out = pt.to_vec();
144 key.seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)
145 .expect("encryption failed");
146
147 in_out.extend_from_slice(&nonce_bytes);
149 in_out
150}
151
152fn open(key: &[u8; 32], nonce12: &[u8; 12], ct_without_suffix: &[u8]) -> Result<Vec<u8>, ()> {
159 let unbound_key = UnboundKey::new(&AES_256_GCM, key).map_err(|_| ())?;
160 let key = LessSafeKey::new(unbound_key);
161
162 let nonce = Nonce::try_assume_unique_for_key(nonce12).map_err(|_| ())?;
165
166 let mut in_out = ct_without_suffix.to_vec();
167 key.open_in_place(nonce, Aad::empty(), &mut in_out)
168 .map_err(|_| ())?;
169
170 in_out.truncate(in_out.len() - 16);
172 Ok(in_out)
173}