rustls/msgs/
persist.rs

1use crate::msgs::base::{PayloadU8, PayloadU16};
2use crate::msgs::codec::{Codec, Reader};
3use crate::msgs::enums::{CipherSuite, ProtocolVersion};
4use crate::msgs::handshake::CertificatePayload;
5use crate::msgs::handshake::SessionID;
6
7use webpki;
8
9use std::cmp;
10use std::mem;
11
12// These are the keys and values we store in session storage.
13
14// --- Client types ---
15/// Keys for session resumption and tickets.
16/// Matching value is a `ClientSessionValue`.
17#[derive(Debug)]
18pub struct ClientSessionKey {
19    kind: &'static [u8],
20    dns_name: PayloadU8,
21}
22
23impl Codec for ClientSessionKey {
24    fn encode(&self, bytes: &mut Vec<u8>) {
25        bytes.extend_from_slice(self.kind);
26        self.dns_name.encode(bytes);
27    }
28
29    // Don't need to read these.
30    fn read(_r: &mut Reader) -> Option<ClientSessionKey> {
31        None
32    }
33}
34
35impl ClientSessionKey {
36    pub fn session_for_dns_name(dns_name: webpki::DNSNameRef) -> ClientSessionKey {
37        let dns_name_str: &str = dns_name.into();
38        ClientSessionKey {
39            kind: b"session",
40            dns_name: PayloadU8::new(dns_name_str.as_bytes().to_vec()),
41        }
42    }
43
44    pub fn hint_for_dns_name(dns_name: webpki::DNSNameRef) -> ClientSessionKey {
45        let dns_name_str: &str = dns_name.into();
46        ClientSessionKey {
47            kind: b"kx-hint",
48            dns_name: PayloadU8::new(dns_name_str.as_bytes().to_vec()),
49        }
50    }
51}
52
53#[derive(Debug)]
54pub struct ClientSessionValue {
55    pub version: ProtocolVersion,
56    pub cipher_suite: CipherSuite,
57    pub session_id: SessionID,
58    pub ticket: PayloadU16,
59    pub master_secret: PayloadU8,
60    pub epoch: u64,
61    pub lifetime: u32,
62    pub age_add: u32,
63    pub extended_ms: bool,
64    pub max_early_data_size: u32,
65    pub server_cert_chain: CertificatePayload,
66}
67
68impl Codec for ClientSessionValue {
69    fn encode(&self, bytes: &mut Vec<u8>) {
70        self.version.encode(bytes);
71        self.cipher_suite.encode(bytes);
72        self.session_id.encode(bytes);
73        self.ticket.encode(bytes);
74        self.master_secret.encode(bytes);
75        self.epoch.encode(bytes);
76        self.lifetime.encode(bytes);
77        self.age_add.encode(bytes);
78        (if self.extended_ms { 1u8 } else { 0u8 }).encode(bytes);
79        self.max_early_data_size.encode(bytes);
80        self.server_cert_chain.encode(bytes);
81    }
82
83    fn read(r: &mut Reader) -> Option<ClientSessionValue> {
84        let v = ProtocolVersion::read(r)?;
85        let cs = CipherSuite::read(r)?;
86        let sid = SessionID::read(r)?;
87        let ticket = PayloadU16::read(r)?;
88        let ms = PayloadU8::read(r)?;
89        let epoch = u64::read(r)?;
90        let lifetime = u32::read(r)?;
91        let age_add = u32::read(r)?;
92        let extended_ms = u8::read(r)?;
93        let max_early_data_size = u32::read(r)?;
94        let server_cert_chain = CertificatePayload::read(r)?;
95
96        Some(ClientSessionValue {
97            version: v,
98            cipher_suite: cs,
99            session_id: sid,
100            ticket,
101            master_secret: ms,
102            epoch,
103            lifetime,
104            age_add,
105            extended_ms: extended_ms == 1u8,
106            max_early_data_size,
107            server_cert_chain,
108        })
109    }
110}
111
112static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60;
113
114impl ClientSessionValue {
115    pub fn new(
116        v: ProtocolVersion,
117        cs: CipherSuite,
118        sessid: &SessionID,
119        ticket: Vec<u8>,
120        ms: Vec<u8>,
121        server_cert_chain: &CertificatePayload,
122    ) -> ClientSessionValue {
123        ClientSessionValue {
124            version: v,
125            cipher_suite: cs,
126            session_id: *sessid,
127            ticket: PayloadU16::new(ticket),
128            master_secret: PayloadU8::new(ms),
129            epoch: 0,
130            lifetime: 0,
131            age_add: 0,
132            extended_ms: false,
133            max_early_data_size: 0,
134            server_cert_chain: server_cert_chain.clone(),
135        }
136    }
137
138    pub fn set_extended_ms_used(&mut self) {
139        self.extended_ms = true;
140    }
141
142    pub fn set_times(&mut self, receipt_time_secs: u64, lifetime_secs: u32, age_add: u32) {
143        self.epoch = receipt_time_secs;
144        self.lifetime = cmp::min(lifetime_secs, MAX_TICKET_LIFETIME);
145        self.age_add = age_add;
146    }
147
148    pub fn has_expired(&self, time_now: u64) -> bool {
149        self.lifetime != 0 && self.epoch + u64::from(self.lifetime) < time_now
150    }
151
152    pub fn get_obfuscated_ticket_age(&self, time_now: u64) -> u32 {
153        let age_secs = time_now.saturating_sub(self.epoch);
154        let age_millis = age_secs as u32 * 1000;
155        age_millis.wrapping_add(self.age_add)
156    }
157
158    pub fn take_ticket(&mut self) -> Vec<u8> {
159        let new_ticket = PayloadU16::new(Vec::new());
160        let old_ticket = mem::replace(&mut self.ticket, new_ticket);
161        old_ticket.0
162    }
163
164    pub fn set_max_early_data_size(&mut self, sz: u32) {
165        self.max_early_data_size = sz;
166    }
167}
168
169// --- Server types ---
170pub type ServerSessionKey = SessionID;
171
172#[derive(Debug)]
173pub struct ServerSessionValue {
174    pub sni: Option<webpki::DNSName>,
175    pub version: ProtocolVersion,
176    pub cipher_suite: CipherSuite,
177    pub master_secret: PayloadU8,
178    pub extended_ms: bool,
179    pub client_cert_chain: Option<CertificatePayload>,
180    pub alpn: Option<PayloadU8>,
181    pub application_data: PayloadU16,
182}
183
184impl Codec for ServerSessionValue {
185    fn encode(&self, bytes: &mut Vec<u8>) {
186        if let Some(ref sni) = self.sni {
187            1u8.encode(bytes);
188            let sni_bytes: &str = sni.as_ref().into();
189            PayloadU8::new(Vec::from(sni_bytes)).encode(bytes);
190        } else {
191            0u8.encode(bytes);
192        }
193        self.version.encode(bytes);
194        self.cipher_suite.encode(bytes);
195        self.master_secret.encode(bytes);
196        (if self.extended_ms { 1u8 } else { 0u8 }).encode(bytes);
197        if let Some(ref chain) = self.client_cert_chain {
198            1u8.encode(bytes);
199            chain.encode(bytes);
200        } else {
201            0u8.encode(bytes);
202        }
203        if let Some(ref alpn) = self.alpn {
204            1u8.encode(bytes);
205            alpn.encode(bytes);
206        } else {
207            0u8.encode(bytes);
208        }
209        self.application_data.encode(bytes);
210    }
211
212    fn read(r: &mut Reader) -> Option<ServerSessionValue> {
213        let has_sni = u8::read(r)?;
214        let sni = if has_sni == 1 {
215            let dns_name = PayloadU8::read(r)?;
216            let dns_name = webpki::DNSNameRef::try_from_ascii(&dns_name.0).ok()?;
217            Some(dns_name.into())
218        } else {
219            None
220        };
221        let v = ProtocolVersion::read(r)?;
222        let cs = CipherSuite::read(r)?;
223        let ms = PayloadU8::read(r)?;
224        let ems = u8::read(r)?;
225        let has_ccert = u8::read(r)? == 1;
226        let ccert = if has_ccert {
227            Some(CertificatePayload::read(r)?)
228        } else {
229            None
230        };
231        let has_alpn = u8::read(r)? == 1;
232        let alpn = if has_alpn {
233            Some(PayloadU8::read(r)?)
234        } else {
235            None
236        };
237        let application_data = PayloadU16::read(r)?;
238
239        Some(ServerSessionValue {
240            sni,
241            version: v,
242            cipher_suite: cs,
243            master_secret: ms,
244            extended_ms: ems == 1u8,
245            client_cert_chain: ccert,
246            alpn,
247            application_data,
248        })
249    }
250}
251
252impl ServerSessionValue {
253    pub fn new(
254        sni: Option<&webpki::DNSName>,
255        v: ProtocolVersion,
256        cs: CipherSuite,
257        ms: Vec<u8>,
258        cert_chain: &Option<CertificatePayload>,
259        alpn: Option<Vec<u8>>,
260        application_data: Vec<u8>,
261    ) -> ServerSessionValue {
262        ServerSessionValue {
263            sni: sni.cloned(),
264            version: v,
265            cipher_suite: cs,
266            master_secret: PayloadU8::new(ms),
267            extended_ms: false,
268            client_cert_chain: cert_chain.clone(),
269            alpn: alpn.map(PayloadU8::new),
270            application_data: PayloadU16::new(application_data),
271        }
272    }
273
274    pub fn set_extended_ms_used(&mut self) {
275        self.extended_ms = true;
276    }
277}