1use crate::config::Config;
9use crate::crypto::ct_eq;
10use crate::params::Cid;
11use std::net::SocketAddr;
12use std::time::{Duration, Instant};
13
14const RETRY: u8 = 1;
15const NEW_TOKEN: u8 = 2;
16const MAC_LEN: usize = 16;
17
18fn addr_bytes(a: &SocketAddr, with_port: bool, out: &mut Vec<u8>) {
19 match a {
20 SocketAddr::V4(v) => out.extend_from_slice(&v.ip().octets()),
21 SocketAddr::V6(v) => out.extend_from_slice(&v.ip().octets()),
22 }
23 if with_port {
24 out.extend_from_slice(&a.port().to_be_bytes());
25 }
26}
27
28fn millis(cfg: &Config, now: Instant) -> u64 {
29 let epoch = *cfg.epoch.get_or_init(|| now);
30 now.saturating_duration_since(epoch).as_millis() as u64
31}
32
33fn mac(cfg: &Config, body: &[u8], addr: &SocketAddr, with_port: bool) -> [u8; 32] {
34 let mut a = Vec::with_capacity(18);
35 addr_bytes(addr, with_port, &mut a);
36 cfg.provider.hmac(&cfg.token_key, &[b"token", body, &a])
37}
38
39fn seal(cfg: &Config, ty: u8, now: Instant, odcid: &[u8], rscid: &[u8], addr: &SocketAddr) -> Vec<u8> {
40 let mut t = Vec::with_capacity(11 + odcid.len() + rscid.len() + MAC_LEN);
41 t.push(ty);
42 t.extend_from_slice(&millis(cfg, now).to_be_bytes());
43 t.push(odcid.len() as u8);
44 t.extend_from_slice(odcid);
45 t.push(rscid.len() as u8);
46 t.extend_from_slice(rscid);
47 let m = mac(cfg, &t, addr, ty == RETRY);
48 t.extend_from_slice(&m[..MAC_LEN]);
49 t
50}
51
52pub fn retry_token(cfg: &Config, addr: SocketAddr, now: Instant, odcid: &[u8], rscid: &[u8]) -> Vec<u8> {
54 seal(cfg, RETRY, now, odcid, rscid, &addr)
55}
56
57pub fn new_token(cfg: &Config, addr: SocketAddr, now: Instant) -> Vec<u8> {
59 seal(cfg, NEW_TOKEN, now, &[], &[], &addr)
60}
61
62#[derive(Debug, PartialEq, Eq)]
64pub enum Validated {
65 Retry { odcid: Cid, rscid: Cid },
67 NewToken,
68}
69
70pub fn validate(cfg: &Config, token: &[u8], addr: SocketAddr, now: Instant) -> Option<Validated> {
72 if token.len() < 11 + MAC_LEN {
73 return None;
74 }
75 let (body, m) = token.split_at(token.len() - MAC_LEN);
76 let ty = body[0];
77 if ty != RETRY && ty != NEW_TOKEN {
78 return None;
79 }
80 let expect = mac(cfg, body, &addr, ty == RETRY);
81 if !ct_eq(&expect[..MAC_LEN], m) {
82 return None;
83 }
84 let issued = u64::from_be_bytes(body[1..9].try_into().unwrap());
85 let lifetime = if ty == RETRY {
86 cfg.retry_token_lifetime
87 } else {
88 cfg.new_token_lifetime.unwrap_or(Duration::ZERO)
89 };
90 let age = millis(cfg, now).checked_sub(issued)?;
91 if age > lifetime.as_millis() as u64 {
92 return None;
93 }
94 if ty == NEW_TOKEN {
95 return Some(Validated::NewToken);
96 }
97 let rest = &body[9..];
98 let ol = *rest.first()? as usize;
99 if ol > 20 || rest.len() < 1 + ol + 1 {
100 return None;
101 }
102 let odcid = Cid::new(&rest[1..1 + ol]);
103 let rl = rest[1 + ol] as usize;
104 if rl > 20 || rest.len() != 2 + ol + rl {
105 return None;
106 }
107 let rscid = Cid::new(&rest[2 + ol..]);
108 Some(Validated::Retry { odcid, rscid })
109}