Coverage Report

Created: 2026-05-18 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/git/checkouts/nss-rs-71e20fe79ef91440/9b94ca3/src/agent.rs
Line
Count
Source
1
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4
// option. This file may not be copied, modified, or distributed
5
// except according to those terms.
6
7
#![expect(
8
    clippy::unwrap_used,
9
    reason = "Let's assume the use of `unwrap` was checked when the use of `unsafe` was reviewed."
10
)]
11
12
use std::{
13
    cell::RefCell,
14
    convert::{TryFrom as _, TryInto as _},
15
    ffi::{CStr, CString},
16
    fmt::{self, Debug, Display, Formatter, Write as _},
17
    mem::MaybeUninit,
18
    ops::{Deref, DerefMut},
19
    os::raw::{c_uint, c_void},
20
    pin::Pin,
21
    ptr::{NonNull, null, null_mut},
22
    rc::Rc,
23
    slice,
24
    time::Instant,
25
};
26
27
use log::{debug, info, trace, warn};
28
29
use crate::{
30
    SECItem, SECItemArray, SECItemBorrowed, SECStatus,
31
    agentio::{AgentIo, METHODS},
32
    assert_initialized,
33
    auth::AuthenticationStatus,
34
    constants::{
35
        Alert, Cipher, Epoch, Extension, Group, SignatureScheme, TLS_VERSION_1_3, Version,
36
    },
37
    ech,
38
    err::{Error, PRErrorCode, Res, is_blocked, secstatus_to_res},
39
    ext::{ExtensionHandler, ExtensionTracker, SSL_CallExtensionWriterOnEchInner},
40
    nss_prelude::{SECItemStr, SECWouldBlock},
41
    null_safe_slice,
42
    p11::{self, PrivateKey, PublicKey, hex_with_len},
43
    prio,
44
    replay::AntiReplay,
45
    secrets::SecretHolder,
46
    ssl::{self, PRBool},
47
    time::{Time, TimeHolder},
48
};
49
pub use crate::{
50
    agentio::{Record, RecordList, as_c_void},
51
    cert::CertificateInfo,
52
};
53
54
/// Private trait for Certificate Compression implementation
55
/// Use `SafeCertCompression` to implement an encoder/decoder instead.
56
trait UnsafeCertCompression {
57
    extern "C" fn decode_callback(
58
        input: *const SECItem,
59
        output: *mut ::std::os::raw::c_uchar,
60
        output_len: usize,
61
        used_len: *mut usize,
62
    ) -> SECStatus;
63
64
    extern "C" fn encode_callback(input: *const SECItem, output: *mut SECItem) -> SECStatus;
65
}
66
67
/// The trait is used to represent a certificate compression data structure
68
/// Used in order to enable Certificate Compression extension during TLS connection
69
pub trait CertificateCompressor {
70
    /// Certificate Compression identifier as in RFC8879
71
    const ID: u16;
72
    /// Certification Compression name (used only for logging/debugging)
73
    const NAME: &CStr;
74
    /// Certificate Compression could be used to encode and decode a certificate
75
    /// though the encoding is not frequently used
76
    /// Enable decoding field is used to signal to the implementation
77
    /// to use the encoding as well
78
    const ENABLE_ENCODING: bool = false;
79
80
    /// Certificate Compression encoding function
81
    ///
82
    /// This default implementation effectively does nothing.
83
    /// However, this is only run if `ENABLE_ENCODING` is `true`.
84
    /// Implementations that set `ENABLE_ENCODING` to `true` need to implement this function.
85
    ///
86
    /// # Errors
87
    /// Encoding was unsuccessful, for example, not enough memory
88
0
    fn encode(input: &[u8], output: &mut [u8]) -> Res<usize> {
89
0
        let len = std::cmp::min(input.len(), output.len());
90
0
        output[..len].copy_from_slice(&input[..len]);
91
0
        Ok(len)
92
0
    }
93
94
    /// Certificate Compression decoding function.
95
    /// # Errors
96
    /// Decoding was unsuccessful.
97
    /// We require a decoder internally to check the length of the decoded buffer.
98
    /// If the decoded length is not equal to the length of the provided slice
99
    /// the decoder should return an error.
100
    fn decode(input: &[u8], output: &mut [u8]) -> Res<()>;
101
}
102
103
/// The trait is responsible for calling `CertificateCompression` encoding and decoding
104
/// functions using the NSS types
105
impl<T: CertificateCompressor> UnsafeCertCompression for T {
106
0
    extern "C" fn decode_callback(
107
0
        input: *const SECItem,
108
0
        output: *mut ::std::os::raw::c_uchar,
109
0
        output_len: usize,
110
0
        used_len: *mut usize,
111
0
    ) -> SECStatus {
112
0
        let Some(input) = NonNull::new(input.cast_mut()) else {
113
0
            return ssl::SECFailure;
114
        };
115
0
        if unsafe { input.as_ref().data.is_null() || input.as_ref().len == 0 } {
116
0
            return ssl::SECFailure;
117
0
        }
118
119
0
        let input_slice = unsafe { null_safe_slice(input.as_ref().data, input.as_ref().len) };
120
0
        let output_slice = unsafe { slice::from_raw_parts_mut(output, output_len) };
121
122
0
        if T::decode(input_slice, output_slice).is_err() {
123
0
            return ssl::SECFailure;
124
0
        }
125
126
0
        unsafe {
127
0
            *used_len = output_len;
128
0
        }
129
0
        ssl::SECSuccess
130
0
    }
131
132
0
    extern "C" fn encode_callback(input: *const SECItem, output: *mut SECItem) -> SECStatus {
133
0
        let Some(input) = NonNull::new(input.cast_mut()) else {
134
0
            return ssl::SECFailure;
135
        };
136
137
0
        let (input_data, input_len) = unsafe {
138
0
            let input_ref = input.as_ref();
139
0
            (input_ref.data, input_ref.len)
140
0
        };
141
142
0
        if input_data.is_null() || input_len == 0 {
143
0
            return ssl::SECFailure;
144
0
        }
145
0
        let input_slice = unsafe { null_safe_slice(input_data, input_len) };
146
147
0
        unsafe {
148
0
            p11::SECITEM_AllocItem(
149
0
                null_mut(),
150
0
                // p11::SECItem is the same as ssl::SECItem
151
0
                output.cast::<SECItemStr>(),
152
0
                // Compression shouldn't make the thing *longer*,
153
0
                // but allocate one extra byte anyway to enable simple testing modes.
154
0
                input_len + 1,
155
0
            );
156
0
        }
157
158
0
        if unsafe { (*output).data.is_null() } {
159
0
            return ssl::SECFailure;
160
0
        }
161
162
0
        let Ok(output_len) = (unsafe { (*output).len.try_into() }) else {
163
0
            return ssl::SECFailure;
164
        };
165
166
0
        let output_slice = unsafe { slice::from_raw_parts_mut((*output).data, output_len) };
167
168
0
        let Ok(encoded_len) = T::encode(input_slice, output_slice) else {
169
0
            return ssl::SECFailure;
170
        };
171
172
0
        if encoded_len == 0 || encoded_len > output_len {
173
0
            return ssl::SECFailure;
174
0
        }
175
176
0
        let Ok(encoded_len) = encoded_len.try_into() else {
177
0
            return ssl::SECFailure;
178
        };
179
180
0
        unsafe {
181
0
            (*output).len = encoded_len;
182
0
        }
183
0
        ssl::SECSuccess
184
0
    }
185
}
186
187
/// The maximum number of tickets to remember for a given connection.
188
const MAX_TICKETS: usize = 4;
189
190
#[must_use]
191
0
pub fn hex_snip_middle<A: AsRef<[u8]>>(buf: A) -> String {
192
    const SHOW_LEN: usize = 8;
193
0
    let buf = buf.as_ref();
194
0
    if buf.len() <= SHOW_LEN * 2 {
195
0
        hex_with_len(buf)
196
    } else {
197
0
        let mut ret = String::with_capacity(SHOW_LEN * 2 + 16);
198
0
        write!(&mut ret, "[{}]: ", buf.len()).expect("write OK");
199
0
        for b in &buf[..SHOW_LEN] {
200
0
            write!(&mut ret, "{b:02x}").expect("write OK");
201
0
        }
202
0
        ret.push_str("..");
203
0
        for b in &buf[buf.len() - SHOW_LEN..] {
204
0
            write!(&mut ret, "{b:02x}").expect("write OK");
205
0
        }
206
0
        ret
207
    }
208
0
}
209
210
#[derive(Clone, Debug, PartialEq, Eq)]
211
pub enum HandshakeState {
212
    New,
213
    InProgress,
214
    AuthenticationPending,
215
    /// When encrypted client hello is enabled, the server might engage a fallback.
216
    /// This is the status that is returned.  The included value is the public
217
    /// name of the server, which should be used to validate the certificate.
218
    EchFallbackAuthenticationPending(String),
219
    Authenticated(PRErrorCode),
220
    Complete(SecretAgentInfo),
221
    Failed(Error),
222
}
223
224
impl HandshakeState {
225
    #[must_use]
226
0
    pub const fn is_connected(&self) -> bool {
227
0
        matches!(self, Self::Complete(_))
228
0
    }
229
230
    #[must_use]
231
484
    pub const fn is_final(&self) -> bool {
232
484
        matches!(self, Self::Complete(_) | Self::Failed(_))
233
484
    }
234
235
    #[must_use]
236
0
    pub const fn authentication_needed(&self) -> bool {
237
0
        matches!(
238
0
            self,
239
            Self::AuthenticationPending | Self::EchFallbackAuthenticationPending(_)
240
        )
241
0
    }
242
}
243
244
2.25k
fn get_alpn(fd: *mut prio::PRFileDesc, pre: bool) -> Res<Option<String>> {
245
2.25k
    let mut alpn_state = ssl::SSLNextProtoState::SSL_NEXT_PROTO_NO_SUPPORT;
246
2.25k
    let mut chosen = vec![0_u8; 255];
247
2.25k
    let mut chosen_len: c_uint = 0;
248
2.25k
    secstatus_to_res(unsafe {
249
2.25k
        ssl::SSL_GetNextProto(
250
2.25k
            fd,
251
2.25k
            &raw mut alpn_state,
252
2.25k
            chosen.as_mut_ptr(),
253
2.25k
            &raw mut chosen_len,
254
2.25k
            c_uint::try_from(chosen.len())?,
255
        )
256
0
    })?;
257
258
2.25k
    let alpn = match (pre, alpn_state) {
259
        (true, ssl::SSLNextProtoState::SSL_NEXT_PROTO_EARLY_VALUE)
260
        | (
261
            false,
262
            ssl::SSLNextProtoState::SSL_NEXT_PROTO_NEGOTIATED
263
            | ssl::SSLNextProtoState::SSL_NEXT_PROTO_SELECTED,
264
        ) => {
265
0
            chosen.truncate(usize::try_from(chosen_len)?);
266
0
            Some(match String::from_utf8(chosen) {
267
0
                Ok(a) => a,
268
0
                Err(_) => return Err(Error::Internal),
269
            })
270
        }
271
2.25k
        _ => None,
272
    };
273
2.25k
    trace!("[{fd:p}] got ALPN {alpn:?}");
274
2.25k
    Ok(alpn)
275
2.25k
}
276
277
pub struct SecretAgentPreInfo {
278
    info: ssl::SSLPreliminaryChannelInfo,
279
    alpn: Option<String>,
280
}
281
282
macro_rules! preinfo_arg {
283
    ($v:ident, $m:ident, $f:ident: $t:ty $(,)?) => {
284
        #[must_use]
285
2.25k
        pub fn $v(&self) -> Option<$t> {
286
2.25k
            match self.info.valuesSet & ssl::$m {
287
1.77k
                0 => None,
288
                _ => Some(
289
484
                    <$t>::try_from(self.info.$f)
290
484
                        .inspect_err(|e| debug!("Invalid value in preinfo: {e:?}"))
Unexecuted instantiation: <nss_rs::agent::SecretAgentPreInfo>::cipher_suite::{closure#0}
Unexecuted instantiation: <nss_rs::agent::SecretAgentPreInfo>::early_data_cipher::{closure#0}
Unexecuted instantiation: <nss_rs::agent::SecretAgentPreInfo>::version::{closure#0}
291
484
                        .ok()?,
292
                ),
293
            }
294
2.25k
        }
<nss_rs::agent::SecretAgentPreInfo>::cipher_suite
Line
Count
Source
285
484
        pub fn $v(&self) -> Option<$t> {
286
484
            match self.info.valuesSet & ssl::$m {
287
0
                0 => None,
288
                _ => Some(
289
484
                    <$t>::try_from(self.info.$f)
290
484
                        .inspect_err(|e| debug!("Invalid value in preinfo: {e:?}"))
291
484
                        .ok()?,
292
                ),
293
            }
294
484
        }
<nss_rs::agent::SecretAgentPreInfo>::early_data_cipher
Line
Count
Source
285
1.77k
        pub fn $v(&self) -> Option<$t> {
286
1.77k
            match self.info.valuesSet & ssl::$m {
287
1.77k
                0 => None,
288
                _ => Some(
289
0
                    <$t>::try_from(self.info.$f)
290
0
                        .inspect_err(|e| debug!("Invalid value in preinfo: {e:?}"))
291
0
                        .ok()?,
292
                ),
293
            }
294
1.77k
        }
Unexecuted instantiation: <nss_rs::agent::SecretAgentPreInfo>::version
295
    };
296
}
297
298
impl SecretAgentPreInfo {
299
2.25k
    fn new(fd: *mut prio::PRFileDesc) -> Res<Self> {
300
2.25k
        let mut info: MaybeUninit<ssl::SSLPreliminaryChannelInfo> = MaybeUninit::uninit();
301
2.25k
        secstatus_to_res(unsafe {
302
2.25k
            ssl::SSL_GetPreliminaryChannelInfo(
303
2.25k
                fd,
304
2.25k
                info.as_mut_ptr(),
305
2.25k
                c_uint::try_from(size_of::<ssl::SSLPreliminaryChannelInfo>())?,
306
            )
307
0
        })?;
308
309
        Ok(Self {
310
2.25k
            info: unsafe { info.assume_init() },
311
2.25k
            alpn: get_alpn(fd, true)?,
312
        })
313
2.25k
    }
314
315
    preinfo_arg!(version, ssl_preinfo_version, protocolVersion: Version);
316
    preinfo_arg!(cipher_suite, ssl_preinfo_cipher_suite, cipherSuite: Cipher);
317
    preinfo_arg!(
318
        early_data_cipher,
319
        ssl_preinfo_0rtt_cipher_suite,
320
        zeroRttCipherSuite: Cipher,
321
    );
322
323
    #[must_use]
324
0
    pub const fn early_data(&self) -> bool {
325
0
        self.info.canSendEarlyData != 0
326
0
    }
327
328
    /// # Errors
329
    ///
330
    /// If `usize` is less than 32 bits and the value is too large.
331
0
    pub fn max_early_data(&self) -> Res<usize> {
332
0
        Ok(usize::try_from(self.info.maxEarlyDataSize)?)
333
0
    }
334
335
    /// Was ECH accepted.
336
    #[must_use]
337
0
    pub const fn ech_accepted(&self) -> Option<bool> {
338
0
        if self.info.valuesSet & ssl::ssl_preinfo_ech == 0 {
339
0
            None
340
        } else {
341
0
            Some(self.info.echAccepted != 0)
342
        }
343
0
    }
344
345
    /// Get the ECH public name that was used.  This will only be available
346
    /// (that is, not `None`) if `ech_accepted()` returns `false`.
347
    /// In this case, certificate validation needs to use this name rather
348
    /// than the original name to validate the certificate.  If
349
    /// that validation passes (that is, `SecretAgent::authenticated` is called
350
    /// with `AuthenticationStatus::Ok`), then the handshake will still fail.
351
    /// After the failed handshake, the state will be `Error::EchRetry`,
352
    /// which contains a valid ECH configuration.
353
    ///
354
    /// # Errors
355
    ///
356
    /// When the public name is not valid UTF-8.  (Note: names should be ASCII.)
357
0
    pub fn ech_public_name(&self) -> Res<Option<&str>> {
358
0
        if self.info.valuesSet & ssl::ssl_preinfo_ech == 0 || self.info.echPublicName.is_null() {
359
0
            Ok(None)
360
        } else {
361
0
            let n = unsafe { CStr::from_ptr(self.info.echPublicName) };
362
0
            Ok(Some(n.to_str()?))
363
        }
364
0
    }
365
366
    #[must_use]
367
0
    pub fn alpn(&self) -> Option<&str> {
368
0
        self.alpn.as_deref()
369
0
    }
370
}
371
372
#[derive(Clone, Debug, Default, PartialEq, Eq)]
373
pub struct SecretAgentInfo {
374
    version: Version,
375
    cipher: Cipher,
376
    group: Group,
377
    resumed: bool,
378
    early_data: bool,
379
    ech_accepted: bool,
380
    alpn: Option<String>,
381
    signature_scheme: SignatureScheme,
382
}
383
384
impl SecretAgentInfo {
385
0
    fn new(fd: *mut prio::PRFileDesc) -> Res<Self> {
386
0
        let mut info: MaybeUninit<ssl::SSLChannelInfo> = MaybeUninit::uninit();
387
0
        secstatus_to_res(unsafe {
388
0
            ssl::SSL_GetChannelInfo(
389
0
                fd,
390
0
                info.as_mut_ptr(),
391
0
                c_uint::try_from(size_of::<ssl::SSLChannelInfo>())?,
392
            )
393
0
        })?;
394
0
        let info = unsafe { info.assume_init() };
395
        Ok(Self {
396
0
            version: info.protocolVersion,
397
0
            cipher: info.cipherSuite,
398
0
            group: Group::try_from(info.keaGroup)?,
399
0
            resumed: info.resumed != 0,
400
0
            early_data: info.earlyDataAccepted != 0,
401
0
            ech_accepted: info.echAccepted != 0,
402
0
            alpn: get_alpn(fd, false)?,
403
0
            signature_scheme: SignatureScheme::try_from(info.signatureScheme)?,
404
        })
405
0
    }
406
    #[must_use]
407
0
    pub const fn version(&self) -> Version {
408
0
        self.version
409
0
    }
410
    #[must_use]
411
0
    pub const fn cipher_suite(&self) -> Cipher {
412
0
        self.cipher
413
0
    }
414
    #[must_use]
415
0
    pub const fn key_exchange(&self) -> Group {
416
0
        self.group
417
0
    }
418
    #[must_use]
419
0
    pub const fn resumed(&self) -> bool {
420
0
        self.resumed
421
0
    }
422
    #[must_use]
423
0
    pub const fn early_data_accepted(&self) -> bool {
424
0
        self.early_data
425
0
    }
426
    #[must_use]
427
0
    pub const fn ech_accepted(&self) -> bool {
428
0
        self.ech_accepted
429
0
    }
430
    #[must_use]
431
0
    pub fn alpn(&self) -> Option<&str> {
432
0
        self.alpn.as_deref()
433
0
    }
434
    #[must_use]
435
0
    pub const fn signature_scheme(&self) -> SignatureScheme {
436
0
        self.signature_scheme
437
0
    }
438
}
439
440
/// `SecretAgent` holds the common parts of client and server.
441
#[derive(Debug)]
442
#[expect(clippy::module_name_repetitions, reason = "This is OK.")]
443
pub struct SecretAgent {
444
    fd: *mut prio::PRFileDesc,
445
    secrets: SecretHolder,
446
    raw: Option<bool>,
447
    io: Pin<Box<AgentIo>>,
448
    state: HandshakeState,
449
450
    /// Records whether authentication of certificates is required.
451
    auth_required: Pin<Box<bool>>,
452
    /// Records any fatal alert that is sent by the stack.
453
    alert: Pin<Box<Option<Alert>>>,
454
    /// The current time.
455
    now: TimeHolder,
456
457
    extension_handlers: Vec<ExtensionTracker>,
458
459
    /// The encrypted client hello (ECH) configuration that is in use.
460
    /// Empty if ECH is not enabled.
461
    ech_config: Vec<u8>,
462
}
463
464
impl SecretAgent {
465
2.57k
    fn new() -> Res<Self> {
466
2.57k
        let mut io = Box::pin(AgentIo::default());
467
2.57k
        let fd = Self::create_fd(&mut io)?;
468
2.57k
        Ok(Self {
469
2.57k
            fd,
470
2.57k
            secrets: SecretHolder::default(),
471
2.57k
            raw: None,
472
2.57k
            io,
473
2.57k
            state: HandshakeState::New,
474
2.57k
475
2.57k
            auth_required: Box::pin(false),
476
2.57k
            alert: Box::pin(None),
477
2.57k
            now: TimeHolder::default(),
478
2.57k
479
2.57k
            extension_handlers: Vec::new(),
480
2.57k
481
2.57k
            ech_config: Vec::new(),
482
2.57k
        })
483
2.57k
    }
484
485
    // Create a new SSL file descriptor.
486
    //
487
    // Note that we create separate bindings for PRFileDesc as both
488
    // ssl::PRFileDesc and prio::PRFileDesc.  This keeps the bindings
489
    // minimal, but it means that the two forms need casts to translate
490
    // between them.  ssl::PRFileDesc is left as an opaque type, as the
491
    // ssl::SSL_* APIs only need an opaque type.
492
2.57k
    fn create_fd(io: &mut Pin<Box<AgentIo>>) -> Res<*mut prio::PRFileDesc> {
493
2.57k
        assert_initialized();
494
2.57k
        let label = CString::new("sslwrapper")?;
495
2.57k
        let id = unsafe { prio::PR_GetUniqueIdentity(label.as_ptr()) };
496
497
2.57k
        let base_fd = unsafe { prio::PR_CreateIOLayerStub(id, METHODS) };
498
2.57k
        if base_fd.is_null() {
499
0
            return Err(Error::CreateSslSocket);
500
2.57k
        }
501
2.57k
        let fd = unsafe {
502
2.57k
            (*base_fd).secret = as_c_void(io).cast();
503
2.57k
            ssl::SSL_ImportFD(null_mut(), base_fd.cast())
504
        };
505
2.57k
        if fd.is_null() {
506
0
            unsafe {
507
0
                prio::PR_Close(base_fd);
508
0
            }
509
0
            return Err(Error::CreateSslSocket);
510
2.57k
        }
511
2.57k
        Ok(fd)
512
2.57k
    }
513
514
0
    unsafe extern "C" fn auth_complete_hook(
515
0
        arg: *mut c_void,
516
0
        _fd: *mut prio::PRFileDesc,
517
0
        _check_sig: PRBool,
518
0
        _is_server: PRBool,
519
0
    ) -> SECStatus {
520
0
        let auth_required_ptr = arg.cast::<bool>();
521
0
        unsafe {
522
0
            *auth_required_ptr = true;
523
0
        }
524
        // NSS insists on getting SECWouldBlock here rather than accepting
525
        // the usual combination of PR_WOULD_BLOCK_ERROR and SECFailure.
526
0
        SECWouldBlock
527
0
    }
528
529
0
    unsafe extern "C" fn alert_sent_cb(
530
0
        fd: *const prio::PRFileDesc,
531
0
        arg: *mut c_void,
532
0
        alert: *const ssl::SSLAlert,
533
0
    ) {
534
0
        let alert = unsafe { alert.as_ref().unwrap() };
535
0
        if alert.level == 2 {
536
            // Fatal alerts demand attention.
537
0
            let st = unsafe { arg.cast::<Option<Alert>>().as_mut().unwrap() };
538
0
            if st.is_none() {
539
0
                *st = Some(alert.description);
540
0
            } else {
541
0
                warn!("[{fd:p}] duplicate alert {}", alert.description);
542
            }
543
0
        }
544
0
    }
545
546
    // Ready this for connecting.
547
2.57k
    fn ready(&mut self, is_server: bool, grease: bool) -> Res<()> {
548
2.57k
        secstatus_to_res(unsafe {
549
2.57k
            ssl::SSL_AuthCertificateHook(
550
2.57k
                self.fd,
551
2.57k
                Some(Self::auth_complete_hook),
552
2.57k
                as_c_void(&mut self.auth_required),
553
            )
554
0
        })?;
555
556
2.57k
        secstatus_to_res(unsafe {
557
2.57k
            ssl::SSL_AlertSentCallback(
558
2.57k
                self.fd,
559
2.57k
                Some(Self::alert_sent_cb),
560
2.57k
                as_c_void(&mut self.alert),
561
            )
562
0
        })?;
563
564
2.57k
        self.now.bind(self.fd)?;
565
2.57k
        self.configure(grease)?;
566
2.57k
        secstatus_to_res(unsafe { ssl::SSL_ResetHandshake(self.fd, PRBool::from(is_server)) })
567
2.57k
    }
568
569
    /// Default configuration.
570
    ///
571
    /// # Errors
572
    ///
573
    /// If `set_version_range` fails.
574
2.57k
    fn configure(&mut self, grease: bool) -> Res<()> {
575
2.57k
        self.set_version_range(TLS_VERSION_1_3, TLS_VERSION_1_3)?;
576
2.57k
        self.set_option(ssl::Opt::Locking, false)?;
577
2.57k
        self.set_option(ssl::Opt::Tickets, false)?;
578
2.57k
        self.set_option(ssl::Opt::OcspStapling, true)?;
579
2.57k
        self.set_option(
580
2.57k
            ssl::Opt::Grease,
581
2.57k
            cfg!(not(feature = "disable-random")) && grease,
582
0
        )?;
583
2.57k
        self.set_option(
584
2.57k
            ssl::Opt::EnableChExtensionPermutation,
585
            cfg!(not(feature = "disable-random")),
586
0
        )?;
587
2.57k
        Ok(())
588
2.57k
    }
589
590
    /// Set the versions that are supported.
591
    ///
592
    /// # Errors
593
    ///
594
    /// If the range of versions isn't supported.
595
5.15k
    pub fn set_version_range(&mut self, min: Version, max: Version) -> Res<()> {
596
5.15k
        let range = ssl::SSLVersionRange { min, max };
597
5.15k
        secstatus_to_res(unsafe { ssl::SSL_VersionRangeSet(self.fd, &raw const range) })
598
5.15k
    }
599
600
    /// Enable a set of ciphers.  Note that the order of these is not respected.
601
    ///
602
    /// # Errors
603
    ///
604
    /// If NSS can't enable or disable ciphers.
605
2.57k
    pub fn set_ciphers(&mut self, ciphers: &[Cipher]) -> Res<()> {
606
2.57k
        if self.state != HandshakeState::New {
607
0
            warn!("[{self}] Cannot enable ciphers in state {:?}", self.state);
608
0
            return Err(Error::Internal);
609
2.57k
        }
610
611
2.57k
        let all_ciphers = unsafe { ssl::SSL_GetImplementedCiphers() };
612
2.57k
        let cipher_count = usize::from(unsafe { ssl::SSL_GetNumImplementedCiphers() });
613
183k
        for i in 0..cipher_count {
614
183k
            let p = all_ciphers.wrapping_add(i);
615
183k
            secstatus_to_res(unsafe {
616
183k
                ssl::SSL_CipherPrefSet(self.fd, i32::from(*p), PRBool::from(false))
617
0
            })?;
618
        }
619
620
7.73k
        for c in ciphers {
621
7.73k
            secstatus_to_res(unsafe {
622
7.73k
                ssl::SSL_CipherPrefSet(self.fd, i32::from(*c), PRBool::from(true))
623
0
            })?;
624
        }
625
2.57k
        Ok(())
626
2.57k
    }
627
628
    /// Set key exchange groups.
629
    ///
630
    /// # Errors
631
    ///
632
    /// If the underlying API fails (which shouldn't happen).
633
2.57k
    pub fn set_groups(&mut self, groups: &[Group]) -> Res<()> {
634
        // SSLNamedGroup is a different size to Group, so copy one by one.
635
2.57k
        let group_vec: Vec<_> = groups
636
2.57k
            .iter()
637
10.3k
            .map(|&g| ssl::SSLNamedGroup::Type::from(g))
638
2.57k
            .collect();
639
640
2.57k
        let ptr = group_vec.as_slice().as_ptr();
641
2.57k
        secstatus_to_res(unsafe {
642
2.57k
            ssl::SSL_NamedGroupConfig(self.fd, ptr, c_uint::try_from(group_vec.len())?)
643
        })
644
2.57k
    }
645
646
    /// Set the number of additional key shares that will be sent in the client hello
647
    ///
648
    /// # Errors
649
    ///
650
    /// If the underlying API fails (which shouldn't happen).
651
1.28k
    pub fn send_additional_key_shares(&mut self, count: usize) -> Res<()> {
652
1.28k
        secstatus_to_res(unsafe {
653
1.28k
            ssl::SSL_SendAdditionalKeyShares(self.fd, c_uint::try_from(count)?)
654
        })
655
1.28k
    }
656
657
    /// Set TLS options.
658
    ///
659
    /// # Errors
660
    ///
661
    /// Returns an error if the option or option value is invalid; i.e., never.
662
18.0k
    pub fn set_option(&self, opt: ssl::Opt, value: bool) -> Res<()> {
663
18.0k
        opt.set(self.fd, value)
664
18.0k
    }
665
666
    /// Enable 0-RTT.
667
    ///
668
    /// # Errors
669
    ///
670
    /// See `set_option`.
671
2.57k
    pub fn enable_0rtt(&self) -> Res<()> {
672
2.57k
        self.set_option(ssl::Opt::EarlyData, true)
673
2.57k
    }
674
675
    /// Disable the `EndOfEarlyData` message.
676
    ///
677
    /// # Errors
678
    ///
679
    /// See `set_option`.
680
2.57k
    pub fn disable_end_of_early_data(&self) -> Res<()> {
681
2.57k
        self.set_option(ssl::Opt::SuppressEndOfEarlyData, true)
682
2.57k
    }
683
684
    /// `set_alpn` sets a list of preferred protocols, starting with the most preferred.
685
    /// Though ALPN [RFC7301] permits octet sequences, this only allows for UTF-8-encoded
686
    /// strings.
687
    ///
688
    /// This asserts if no items are provided, or if any individual item is longer than
689
    /// 255 octets in length.
690
    ///
691
    /// # Errors
692
    ///
693
    /// If the list of protocols is empty, contains an empty value, or
694
    /// contains a value longer than 255 bytes.
695
    ///
696
    /// [RFC7301]: https://datatracker.ietf.org/doc/html/rfc7301
697
2.57k
    pub fn set_alpn<A: AsRef<[u8]>>(&mut self, protocols: &[A]) -> Res<()> {
698
        // Prepare to encode.
699
2.57k
        let len = protocols.len() + protocols.iter().map(|p| p.as_ref().len()).sum::<usize>();
<nss_rs::agent::SecretAgent>::set_alpn::<alloc::string::String>::{closure#0}
Line
Count
Source
699
2.57k
        let len = protocols.len() + protocols.iter().map(|p| p.as_ref().len()).sum::<usize>();
Unexecuted instantiation: <nss_rs::agent::SecretAgent>::set_alpn::<_>::{closure#0}
700
2.57k
        let mut encoded = Vec::with_capacity(len);
701
2.57k
        let mut add = |v: &A| -> Res<()> {
702
2.57k
            let v = v.as_ref();
703
2.57k
            u8::try_from(v.len()).map_or(Err(Error::InvalidAlpn), |s| {
704
2.57k
                if s > 0 {
705
2.57k
                    encoded.push(s);
706
2.57k
                    encoded.extend_from_slice(v);
707
2.57k
                    Ok(())
708
                } else {
709
0
                    Err(Error::InvalidAlpn)
710
                }
711
2.57k
            })
<nss_rs::agent::SecretAgent>::set_alpn::<alloc::string::String>::{closure#1}::{closure#0}
Line
Count
Source
703
2.57k
            u8::try_from(v.len()).map_or(Err(Error::InvalidAlpn), |s| {
704
2.57k
                if s > 0 {
705
2.57k
                    encoded.push(s);
706
2.57k
                    encoded.extend_from_slice(v);
707
2.57k
                    Ok(())
708
                } else {
709
0
                    Err(Error::InvalidAlpn)
710
                }
711
2.57k
            })
Unexecuted instantiation: <nss_rs::agent::SecretAgent>::set_alpn::<_>::{closure#1}::{closure#0}
712
2.57k
        };
<nss_rs::agent::SecretAgent>::set_alpn::<alloc::string::String>::{closure#1}
Line
Count
Source
701
2.57k
        let mut add = |v: &A| -> Res<()> {
702
2.57k
            let v = v.as_ref();
703
2.57k
            u8::try_from(v.len()).map_or(Err(Error::InvalidAlpn), |s| {
704
                if s > 0 {
705
                    encoded.push(s);
706
                    encoded.extend_from_slice(v);
707
                    Ok(())
708
                } else {
709
                    Err(Error::InvalidAlpn)
710
                }
711
            })
712
2.57k
        };
Unexecuted instantiation: <nss_rs::agent::SecretAgent>::set_alpn::<_>::{closure#1}
713
714
        // NSS inherited an idiosyncratic API as a result of having implemented NPN
715
        // before ALPN.  For that reason, we need to put the "best" option last.
716
2.57k
        let (first, rest) = protocols.split_first().ok_or(Error::InvalidAlpn)?;
717
2.57k
        for v in rest {
718
0
            add(v)?;
719
        }
720
2.57k
        add(first)?;
721
2.57k
        debug_assert_eq!(len, encoded.len());
722
723
        // Now give the result to NSS.
724
2.57k
        secstatus_to_res(unsafe {
725
2.57k
            ssl::SSL_SetNextProtoNego(
726
2.57k
                self.fd,
727
2.57k
                encoded.as_slice().as_ptr(),
728
2.57k
                c_uint::try_from(encoded.len())?,
729
            )
730
        })
731
2.57k
    }
<nss_rs::agent::SecretAgent>::set_alpn::<alloc::string::String>
Line
Count
Source
697
2.57k
    pub fn set_alpn<A: AsRef<[u8]>>(&mut self, protocols: &[A]) -> Res<()> {
698
        // Prepare to encode.
699
2.57k
        let len = protocols.len() + protocols.iter().map(|p| p.as_ref().len()).sum::<usize>();
700
2.57k
        let mut encoded = Vec::with_capacity(len);
701
2.57k
        let mut add = |v: &A| -> Res<()> {
702
            let v = v.as_ref();
703
            u8::try_from(v.len()).map_or(Err(Error::InvalidAlpn), |s| {
704
                if s > 0 {
705
                    encoded.push(s);
706
                    encoded.extend_from_slice(v);
707
                    Ok(())
708
                } else {
709
                    Err(Error::InvalidAlpn)
710
                }
711
            })
712
        };
713
714
        // NSS inherited an idiosyncratic API as a result of having implemented NPN
715
        // before ALPN.  For that reason, we need to put the "best" option last.
716
2.57k
        let (first, rest) = protocols.split_first().ok_or(Error::InvalidAlpn)?;
717
2.57k
        for v in rest {
718
0
            add(v)?;
719
        }
720
2.57k
        add(first)?;
721
2.57k
        debug_assert_eq!(len, encoded.len());
722
723
        // Now give the result to NSS.
724
2.57k
        secstatus_to_res(unsafe {
725
2.57k
            ssl::SSL_SetNextProtoNego(
726
2.57k
                self.fd,
727
2.57k
                encoded.as_slice().as_ptr(),
728
2.57k
                c_uint::try_from(encoded.len())?,
729
            )
730
        })
731
2.57k
    }
Unexecuted instantiation: <nss_rs::agent::SecretAgent>::set_alpn::<_>
732
733
    /// Install a certificate compression mechanism.
734
    ///
735
    /// # Errors
736
    /// If the compression mechanism with the same id is already registered
737
    /// If too many compression mechanisms are already registered
738
    ///
739
    /// This returns an error if the certificate compression could not be established
740
    ///
741
    /// [RFC8879]: https://datatracker.ietf.org/doc/rfc8879/
742
0
    pub fn set_certificate_compression<T: CertificateCompressor>(&mut self) -> Res<()> {
743
0
        if T::ID == 0 {
744
0
            return Err(Error::InvalidCertificateCompressionID);
745
0
        }
746
747
0
        let compressor: ssl::SSLCertificateCompressionAlgorithm =
748
0
            ssl::SSLCertificateCompressionAlgorithm {
749
0
                id: T::ID,
750
0
                name: T::NAME.as_ptr(),
751
0
                encode: T::ENABLE_ENCODING.then_some(<T as UnsafeCertCompression>::encode_callback),
752
0
                decode: Some(<T as UnsafeCertCompression>::decode_callback),
753
0
            };
754
0
        unsafe { ssl::SSL_SetCertificateCompressionAlgorithm(self.fd, compressor) }
755
0
    }
756
757
    /// Install an extension handler.
758
    ///
759
    /// This can be called multiple times with different values for `ext`.  The handler is provided
760
    /// as `Rc<RefCell<dyn T>>` so that the caller is able to hold a reference to the handler
761
    /// and later access any state that it accumulates.
762
    ///
763
    /// # Errors
764
    ///
765
    /// When the extension handler can't be successfully installed.
766
2.57k
    pub fn extension_handler(
767
2.57k
        &mut self,
768
2.57k
        ext: Extension,
769
2.57k
        handler: Rc<RefCell<dyn ExtensionHandler>>,
770
2.57k
    ) -> Res<()> {
771
2.57k
        let tracker = unsafe { ExtensionTracker::new(self.fd, ext, handler)? };
772
2.57k
        self.extension_handlers.push(tracker);
773
2.57k
        Ok(())
774
2.57k
    }
775
776
    // This function tracks whether handshake() or handshake_raw() was used
777
    // and prevents the other from being used.
778
3.54k
    fn set_raw(&mut self, r: bool) -> Res<()> {
779
3.54k
        if let Some(raw) = self.raw {
780
1.77k
            if raw == r {
781
1.77k
                Ok(())
782
            } else {
783
0
                Err(Error::MixedHandshakeMethod)
784
            }
785
        } else {
786
1.77k
            self.secrets.register(self.fd)?;
787
1.77k
            self.raw = Some(r);
788
1.77k
            Ok(())
789
        }
790
3.54k
    }
791
792
    /// Get information about the connection.
793
    /// This includes the version, ciphersuite, and ALPN.
794
    ///
795
    /// Calling this function returns None until the connection is complete.
796
    #[must_use]
797
484
    pub const fn info(&self) -> Option<&SecretAgentInfo> {
798
484
        match &self.state {
799
0
            HandshakeState::Complete(info) => Some(info),
800
484
            _ => None,
801
        }
802
484
    }
803
804
    /// Get any preliminary information about the status of the connection.
805
    ///
806
    /// This includes whether 0-RTT was accepted and any information related to that.
807
    /// Calling this function collects all the relevant information.
808
    ///
809
    /// # Errors
810
    ///
811
    /// When the underlying socket functions fail.
812
2.25k
    pub fn preinfo(&self) -> Res<SecretAgentPreInfo> {
813
2.25k
        SecretAgentPreInfo::new(self.fd)
814
2.25k
    }
815
816
    /// Get the peer's certificate chain.
817
    #[must_use]
818
0
    pub fn peer_certificate(&self) -> Option<CertificateInfo> {
819
0
        CertificateInfo::new(self.fd)
820
0
    }
821
822
    /// Export keying material per RFC 8446 Section 7.5.
823
    ///
824
    /// This can only be called after the handshake is complete.
825
    /// In TLS 1.3, there is no distinction between no context and an empty
826
    /// context, so the caller passes `&[u8]` instead of `Option<&[u8]>`.
827
    ///
828
    /// # Errors
829
    ///
830
    /// Returns `Error::InvalidState` if the handshake is not complete,
831
    /// `Error::InvalidInput` if `out` is empty, or an NSS error if the
832
    /// export fails.
833
0
    pub fn export_keying_material(&self, label: &[u8], context: &[u8], out: &mut [u8]) -> Res<()> {
834
0
        if !self.state.is_connected() {
835
0
            return Err(Error::InvalidState);
836
0
        }
837
838
0
        if out.is_empty() {
839
0
            return Err(Error::InvalidInput);
840
0
        }
841
842
0
        secstatus_to_res(unsafe {
843
0
            ssl::SSL_ExportKeyingMaterial(
844
0
                self.fd,
845
0
                label.as_ptr().cast(),
846
0
                c_uint::try_from(label.len())?,
847
0
                PRBool::from(!context.is_empty()),
848
0
                context.as_ptr(),
849
0
                c_uint::try_from(context.len())?,
850
0
                out.as_mut_ptr(),
851
0
                c_uint::try_from(out.len())?,
852
            )
853
        })
854
0
    }
855
856
    /// Return any fatal alert that the TLS stack might have sent.
857
    #[must_use]
858
0
    pub fn alert(&self) -> Option<Alert> {
859
0
        *self.alert
860
0
    }
861
862
    /// Call this function to mark the peer as authenticated.
863
    ///
864
    /// # Panics
865
    ///
866
    /// If the handshake doesn't need to be authenticated.
867
0
    pub fn authenticated(&mut self, status: AuthenticationStatus) {
868
0
        assert!(self.state.authentication_needed());
869
0
        *self.auth_required = false;
870
0
        self.state = HandshakeState::Authenticated(status.into());
871
0
    }
872
873
4.03k
    fn capture_error<T>(&mut self, res: Res<T>) -> Res<T> {
874
4.03k
        if let Err(e) = res {
875
0
            let e = ech::convert_ech_error(self.fd, e);
876
0
            warn!("[{self}] error: {e:?}");
877
0
            self.state = HandshakeState::Failed(e.clone());
878
0
            Err(e)
879
        } else {
880
4.03k
            res
881
        }
882
4.03k
    }
<nss_rs::agent::SecretAgent>::capture_error::<core::pin::Pin<alloc::boxed::Box<nss_rs::agentio::RecordList>>>
Line
Count
Source
873
3.54k
    fn capture_error<T>(&mut self, res: Res<T>) -> Res<T> {
874
3.54k
        if let Err(e) = res {
875
0
            let e = ech::convert_ech_error(self.fd, e);
876
0
            warn!("[{self}] error: {e:?}");
877
0
            self.state = HandshakeState::Failed(e.clone());
878
0
            Err(e)
879
        } else {
880
3.54k
            res
881
        }
882
3.54k
    }
Unexecuted instantiation: <nss_rs::agent::SecretAgent>::capture_error::<nss_rs::agent::SecretAgentInfo>
<nss_rs::agent::SecretAgent>::capture_error::<()>
Line
Count
Source
873
484
    fn capture_error<T>(&mut self, res: Res<T>) -> Res<T> {
874
484
        if let Err(e) = res {
875
0
            let e = ech::convert_ech_error(self.fd, e);
876
0
            warn!("[{self}] error: {e:?}");
877
0
            self.state = HandshakeState::Failed(e.clone());
878
0
            Err(e)
879
        } else {
880
484
            res
881
        }
882
484
    }
883
884
1.77k
    fn update_state(&mut self, res: Res<()>) -> Res<()> {
885
1.77k
        self.state = if is_blocked(&res) {
886
1.77k
            if *self.auth_required {
887
0
                self.preinfo()?.ech_public_name()?.map_or(
888
0
                    HandshakeState::AuthenticationPending,
889
0
                    |public_name| {
890
0
                        HandshakeState::EchFallbackAuthenticationPending(public_name.to_owned())
891
0
                    },
892
                )
893
            } else {
894
1.77k
                HandshakeState::InProgress
895
            }
896
        } else {
897
0
            self.capture_error(res)?;
898
0
            let info = self.capture_error(SecretAgentInfo::new(self.fd))?;
899
0
            HandshakeState::Complete(info)
900
        };
901
1.77k
        info!("[{self}] state -> {:?}", self.state);
902
1.77k
        Ok(())
903
1.77k
    }
904
905
    /// Drive the TLS handshake, taking bytes from `input` and putting
906
    /// any bytes necessary into `output`.
907
    /// This takes the current time as `now`.
908
    /// On success a tuple of a `HandshakeState` and usize indicate whether the handshake
909
    /// is complete and how many bytes were written to `output`, respectively.
910
    /// If the state is `HandshakeState::AuthenticationPending`, then ONLY call this
911
    /// function if you want to proceed, because this will mark the certificate as OK.
912
    ///
913
    /// # Errors
914
    ///
915
    /// When the handshake fails this returns an error.
916
0
    pub fn handshake(&mut self, now: Instant, input: &[u8]) -> Res<Vec<u8>> {
917
0
        self.now.set(now)?;
918
0
        self.set_raw(false)?;
919
920
0
        let rv = {
921
            // Within this scope, _h maintains a mutable reference to self.io.
922
0
            let _h = self.io.wrap(input);
923
0
            match self.state {
924
0
                HandshakeState::Authenticated(err) => unsafe {
925
0
                    ssl::SSL_AuthCertificateComplete(self.fd, err)
926
                },
927
0
                _ => unsafe { ssl::SSL_ForceHandshake(self.fd) },
928
            }
929
        };
930
        // Take before updating state so that we leave the output buffer empty
931
        // even if there is an error.
932
0
        let output = self.io.take_output();
933
0
        self.update_state(secstatus_to_res(rv))?;
934
0
        Ok(output)
935
0
    }
936
937
    /// Setup to receive records for raw handshake functions.
938
3.54k
    fn setup_raw(&mut self) -> Res<Pin<Box<RecordList>>> {
939
3.54k
        self.set_raw(true)?;
940
3.54k
        self.capture_error(RecordList::setup(self.fd))
941
3.54k
    }
942
943
    /// Drive the TLS handshake, but get the raw content of records, not
944
    /// protected records as bytes. This function is incompatible with
945
    /// `handshake()`; use either this or `handshake()` exclusively.
946
    ///
947
    /// Ideally, this only includes records from the current epoch.
948
    /// If you send data from multiple epochs, you might end up being sad.
949
    ///
950
    /// # Errors
951
    ///
952
    /// When the handshake fails this returns an error.
953
1.77k
    pub fn handshake_raw(&mut self, now: Instant, input: Option<Record>) -> Res<RecordList> {
954
1.77k
        self.now.set(now)?;
955
1.77k
        let records = self.setup_raw()?;
956
957
        // Fire off any authentication we might need to complete.
958
1.77k
        if let HandshakeState::Authenticated(err) = self.state {
959
0
            let result =
960
0
                secstatus_to_res(unsafe { ssl::SSL_AuthCertificateComplete(self.fd, err) });
961
0
            debug!("[{self}] SSL_AuthCertificateComplete: {result:?}");
962
            // This should return SECSuccess, so don't use update_state().
963
0
            self.capture_error(result)?;
964
1.77k
        }
965
966
        // Feed in any records.
967
1.77k
        if let Some(rec) = input {
968
484
            self.capture_error(rec.write(self.fd))?;
969
1.28k
        }
970
971
        // Drive the handshake once more.
972
1.77k
        let rv = secstatus_to_res(unsafe { ssl::SSL_ForceHandshake(self.fd) });
973
1.77k
        self.update_state(rv)?;
974
975
1.77k
        Ok(*Pin::into_inner(records))
976
1.77k
    }
977
978
    /// # Panics
979
    ///
980
    /// If setup fails.
981
2.57k
    pub fn close(&mut self) {
982
        // It should be safe to close multiple times.
983
2.57k
        if self.fd.is_null() {
984
0
            return;
985
2.57k
        }
986
        #[expect(
987
            clippy::branches_sharing_code,
988
            reason = "The PR_Close calls cannot be run after dropping the returned values."
989
        )]
990
2.57k
        if self.raw == Some(true) {
991
            // Need to hold the record list in scope until the close is done.
992
1.77k
            let _records = self.setup_raw().expect("Can only close");
993
1.77k
            unsafe {
994
1.77k
                prio::PR_Close(self.fd.cast());
995
1.77k
            }
996
        } else {
997
            // Need to hold the IO wrapper in scope until the close is done.
998
805
            let _io = self.io.wrap(&[]);
999
805
            unsafe {
1000
805
                prio::PR_Close(self.fd.cast());
1001
805
            }
1002
        }
1003
2.57k
        let _output = self.io.take_output();
1004
2.57k
        self.fd = null_mut();
1005
2.57k
    }
1006
1007
    /// State returns the status of the handshake.
1008
    #[must_use]
1009
4.03k
    pub const fn state(&self) -> &HandshakeState {
1010
4.03k
        &self.state
1011
4.03k
    }
1012
1013
    /// Check if the indicated secret is ready for installation.
1014
    #[must_use]
1015
484
    pub fn has_secret(&self, epoch: Epoch) -> bool {
1016
484
        self.secrets.has(epoch)
1017
484
    }
1018
1019
    /// Take a read secret.  This will only return a non-`None` value once.
1020
    #[must_use]
1021
484
    pub fn read_secret(&mut self, epoch: Epoch) -> Option<p11::SymKey> {
1022
484
        self.secrets.take_read(epoch)
1023
484
    }
1024
1025
    /// Take a write secret.
1026
    #[must_use]
1027
968
    pub fn write_secret(&mut self, epoch: Epoch) -> Option<p11::SymKey> {
1028
968
        self.secrets.take_write(epoch)
1029
968
    }
1030
1031
    /// Get the active ECH configuration, which is empty if ECH is disabled.
1032
    #[must_use]
1033
0
    pub fn ech_config(&self) -> &[u8] {
1034
0
        &self.ech_config
1035
0
    }
1036
}
1037
1038
impl Drop for SecretAgent {
1039
2.57k
    fn drop(&mut self) {
1040
2.57k
        self.close();
1041
2.57k
    }
1042
}
1043
1044
impl Display for SecretAgent {
1045
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1046
0
        write!(f, "Agent {:p}", self.fd)
1047
0
    }
1048
}
1049
1050
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone)]
1051
pub struct ResumptionToken {
1052
    token: Vec<u8>,
1053
    expiration_time: Instant,
1054
}
1055
1056
impl Debug for ResumptionToken {
1057
0
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1058
0
        f.debug_struct("ResumptionToken")
1059
0
            .field("token", &hex_snip_middle(&self.token))
1060
0
            .field("expiration_time", &self.expiration_time)
1061
0
            .finish()
1062
0
    }
1063
}
1064
1065
impl AsRef<[u8]> for ResumptionToken {
1066
0
    fn as_ref(&self) -> &[u8] {
1067
0
        &self.token
1068
0
    }
1069
}
1070
1071
impl ResumptionToken {
1072
    #[must_use]
1073
0
    pub const fn new(token: Vec<u8>, expiration_time: Instant) -> Self {
1074
0
        Self {
1075
0
            token,
1076
0
            expiration_time,
1077
0
        }
1078
0
    }
1079
1080
    #[must_use]
1081
0
    pub const fn expiration_time(&self) -> Instant {
1082
0
        self.expiration_time
1083
0
    }
1084
}
1085
1086
/// A TLS Client.
1087
#[derive(Debug)]
1088
pub struct Client {
1089
    agent: SecretAgent,
1090
1091
    /// The name of the server we're attempting a connection to.
1092
    server_name: String,
1093
    /// Records the resumption tokens we've received.
1094
    #[expect(clippy::box_collection, reason = "We need the Box.")]
1095
    resumption: Pin<Box<Vec<ResumptionToken>>>,
1096
}
1097
1098
impl Client {
1099
    /// Create a new client agent.
1100
    ///
1101
    /// # Errors
1102
    ///
1103
    /// Errors returned if the socket can't be created or configured.
1104
1.28k
    pub fn new<I: Into<String>>(server_name: I, grease: bool) -> Res<Self> {
1105
1.28k
        let server_name = server_name.into();
1106
1.28k
        let mut agent = SecretAgent::new()?;
1107
1.28k
        let url = CString::new(server_name.as_bytes())?;
1108
1.28k
        secstatus_to_res(unsafe { ssl::SSL_SetURL(agent.fd, url.as_ptr()) })?;
1109
1.28k
        agent.ready(false, grease)?;
1110
1.28k
        let mut client = Self {
1111
1.28k
            agent,
1112
1.28k
            server_name,
1113
1.28k
            resumption: Box::pin(Vec::new()),
1114
1.28k
        };
1115
1.28k
        client.ready()?;
1116
1.28k
        Ok(client)
1117
1.28k
    }
Unexecuted instantiation: <nss_rs::agent::Client>::new::<alloc::string::String>
Unexecuted instantiation: <nss_rs::agent::Client>::new::<alloc::string::String>
Unexecuted instantiation: <nss_rs::agent::Client>::new::<_>
<nss_rs::agent::Client>::new::<alloc::string::String>
Line
Count
Source
1104
484
    pub fn new<I: Into<String>>(server_name: I, grease: bool) -> Res<Self> {
1105
484
        let server_name = server_name.into();
1106
484
        let mut agent = SecretAgent::new()?;
1107
484
        let url = CString::new(server_name.as_bytes())?;
1108
484
        secstatus_to_res(unsafe { ssl::SSL_SetURL(agent.fd, url.as_ptr()) })?;
1109
484
        agent.ready(false, grease)?;
1110
484
        let mut client = Self {
1111
484
            agent,
1112
484
            server_name,
1113
484
            resumption: Box::pin(Vec::new()),
1114
484
        };
1115
484
        client.ready()?;
1116
484
        Ok(client)
1117
484
    }
<nss_rs::agent::Client>::new::<alloc::string::String>
Line
Count
Source
1104
805
    pub fn new<I: Into<String>>(server_name: I, grease: bool) -> Res<Self> {
1105
805
        let server_name = server_name.into();
1106
805
        let mut agent = SecretAgent::new()?;
1107
805
        let url = CString::new(server_name.as_bytes())?;
1108
805
        secstatus_to_res(unsafe { ssl::SSL_SetURL(agent.fd, url.as_ptr()) })?;
1109
805
        agent.ready(false, grease)?;
1110
805
        let mut client = Self {
1111
805
            agent,
1112
805
            server_name,
1113
805
            resumption: Box::pin(Vec::new()),
1114
805
        };
1115
805
        client.ready()?;
1116
805
        Ok(client)
1117
805
    }
1118
1119
0
    unsafe extern "C" fn resumption_token_cb(
1120
0
        fd: *mut prio::PRFileDesc,
1121
0
        token: *const u8,
1122
0
        len: c_uint,
1123
0
        arg: *mut c_void,
1124
0
    ) -> SECStatus {
1125
0
        let mut info: MaybeUninit<ssl::SSLResumptionTokenInfo> = MaybeUninit::uninit();
1126
0
        let Ok(info_len) = c_uint::try_from(size_of::<ssl::SSLResumptionTokenInfo>()) else {
1127
0
            return ssl::SECFailure;
1128
        };
1129
0
        let info_res =
1130
0
            unsafe { ssl::SSL_GetResumptionTokenInfo(token, len, info.as_mut_ptr(), info_len) };
1131
0
        if info_res.is_err() {
1132
            // Ignore the token.
1133
0
            return ssl::SECSuccess;
1134
0
        }
1135
0
        let expiration_time = unsafe { info.assume_init_ref() }.expirationTime;
1136
0
        if unsafe { ssl::SSL_DestroyResumptionTokenInfo(info.as_mut_ptr()) }.is_err() {
1137
            // Ignore the token.
1138
0
            return ssl::SECSuccess;
1139
0
        }
1140
0
        let Some(resumption) = (unsafe { arg.cast::<Vec<ResumptionToken>>().as_mut() }) else {
1141
0
            return ssl::SECFailure;
1142
        };
1143
0
        let Ok(len) = usize::try_from(len) else {
1144
0
            return ssl::SECFailure;
1145
        };
1146
0
        let mut v = Vec::with_capacity(len);
1147
0
        v.extend_from_slice(unsafe { null_safe_slice(token, len) });
1148
0
        debug!("[{fd:p}] Got resumption token {}", hex_snip_middle(&v));
1149
1150
0
        if resumption.len() >= MAX_TICKETS {
1151
0
            resumption.remove(0);
1152
0
        }
1153
0
        if let Ok(t) = Time::try_from(expiration_time) {
1154
0
            resumption.push(ResumptionToken::new(v, *t));
1155
0
        }
1156
0
        ssl::SECSuccess
1157
0
    }
1158
1159
    #[must_use]
1160
0
    pub fn server_name(&self) -> &str {
1161
0
        &self.server_name
1162
0
    }
1163
1164
1.28k
    fn ready(&mut self) -> Res<()> {
1165
1.28k
        let fd = self.fd;
1166
        unsafe {
1167
1.28k
            ssl::SSL_SetResumptionTokenCallback(
1168
1.28k
                fd,
1169
1.28k
                Some(Self::resumption_token_cb),
1170
1.28k
                as_c_void(&mut self.resumption),
1171
            )
1172
        }
1173
1.28k
    }
1174
1175
    /// Take a resumption token.
1176
    #[must_use]
1177
0
    pub fn resumption_token(&mut self) -> Option<ResumptionToken> {
1178
0
        (*self.resumption).pop()
1179
0
    }
1180
1181
    /// Check if there are more resumption tokens.
1182
    #[must_use]
1183
0
    pub fn has_resumption_token(&self) -> bool {
1184
0
        !(*self.resumption).is_empty()
1185
0
    }
1186
1187
    /// Enable resumption, using a token previously provided.
1188
    ///
1189
    /// # Errors
1190
    ///
1191
    /// Error returned when the resumption token is invalid or
1192
    /// the socket is not able to use the value.
1193
0
    pub fn enable_resumption<A: AsRef<[u8]>>(&mut self, token: A) -> Res<()> {
1194
        unsafe {
1195
0
            ssl::SSL_SetResumptionToken(
1196
0
                self.agent.fd,
1197
0
                token.as_ref().as_ptr(),
1198
0
                c_uint::try_from(token.as_ref().len())?,
1199
            )
1200
        }
1201
0
    }
1202
1203
    /// Enable encrypted client hello (ECH), using the encoded `ECHConfigList`.
1204
    ///
1205
    /// When ECH is enabled, a client needs to look for `Error::EchRetry` as a
1206
    /// failure code.  If `Error::EchRetry` is received when connecting, the
1207
    /// connection attempt should be retried and the included value provided
1208
    /// to this function (instead of what is received from DNS).
1209
    ///
1210
    /// Calling this function with an empty value for `ech_config_list` enables
1211
    /// ECH greasing.  When that is done, there is no need to look for `EchRetry`
1212
    ///
1213
    /// # Errors
1214
    ///
1215
    /// Error returned when the configuration is invalid.
1216
0
    pub fn enable_ech<A: AsRef<[u8]>>(&mut self, ech_config_list: A) -> Res<()> {
1217
0
        let config = ech_config_list.as_ref();
1218
0
        debug!("[{self}] Enable ECH for a server: {}", hex_with_len(config));
1219
0
        self.ech_config = Vec::from(config);
1220
0
        if config.is_empty() {
1221
0
            unsafe { ech::SSL_EnableTls13GreaseEch(self.agent.fd, PRBool::from(true)) }
1222
        } else {
1223
            unsafe {
1224
0
                ech::SSL_SetClientEchConfigs(
1225
0
                    self.agent.fd,
1226
0
                    config.as_ptr(),
1227
0
                    c_uint::try_from(config.len())?,
1228
0
                )?;
1229
                // If the ECH configuration is valid, and only then,
1230
                // allow writing of different transport parameters to the inner and outer
1231
                // ClientHello. Avoid setting this otherwise, as the transport
1232
                // parameter extension handler filters out essential values from the
1233
                // outer ClientHello. Under normal operation, NSS reports to
1234
                // extension writers that an ordinary, non-ECH ClientHello is an
1235
                // outer ClientHello, resulting in unwanted filtering.
1236
0
                SSL_CallExtensionWriterOnEchInner(self.fd, PRBool::from(true))
1237
            }
1238
        }
1239
0
    }
1240
}
1241
1242
impl Deref for Client {
1243
    type Target = SecretAgent;
1244
7.73k
    fn deref(&self) -> &SecretAgent {
1245
7.73k
        &self.agent
1246
7.73k
    }
1247
}
1248
1249
impl DerefMut for Client {
1250
9.02k
    fn deref_mut(&mut self) -> &mut SecretAgent {
1251
9.02k
        &mut self.agent
1252
9.02k
    }
1253
}
1254
1255
impl Display for Client {
1256
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1257
0
        write!(f, "Client {:p}", self.agent.fd)
1258
0
    }
1259
}
1260
1261
/// `ZeroRttCheckResult` encapsulates the options for handling a `ClientHello`.
1262
#[derive(Clone, Debug, PartialEq, Eq)]
1263
pub enum ZeroRttCheckResult {
1264
    /// Accept 0-RTT.
1265
    Accept,
1266
    /// Reject 0-RTT, but continue the handshake normally.
1267
    Reject,
1268
    /// Send `HelloRetryRequest` (probably not needed for QUIC).
1269
    HelloRetryRequest(Vec<u8>),
1270
    /// Fail the handshake.
1271
    Fail,
1272
}
1273
1274
/// A `ZeroRttChecker` is used by the agent to validate the application token (as provided by
1275
/// `send_ticket`)
1276
pub trait ZeroRttChecker: Debug + Unpin {
1277
    fn check(&self, token: &[u8]) -> ZeroRttCheckResult;
1278
}
1279
1280
/// Using `AllowZeroRtt` for the implementation of `ZeroRttChecker` means
1281
/// accepting 0-RTT always.  This generally isn't a great idea, so this
1282
/// generates a strong warning when it is used.
1283
#[derive(Debug)]
1284
pub struct AllowZeroRtt {}
1285
impl ZeroRttChecker for AllowZeroRtt {
1286
0
    fn check(&self, _token: &[u8]) -> ZeroRttCheckResult {
1287
0
        warn!("AllowZeroRtt accepting 0-RTT");
1288
0
        ZeroRttCheckResult::Accept
1289
0
    }
1290
}
1291
1292
#[derive(Debug)]
1293
struct ZeroRttCheckState {
1294
    checker: Pin<Box<dyn ZeroRttChecker>>,
1295
}
1296
1297
impl ZeroRttCheckState {
1298
1.28k
    pub fn new(checker: Box<dyn ZeroRttChecker>) -> Self {
1299
1.28k
        Self {
1300
1.28k
            checker: Pin::new(checker),
1301
1.28k
        }
1302
1.28k
    }
1303
}
1304
1305
#[derive(Debug)]
1306
pub struct Server {
1307
    agent: SecretAgent,
1308
    /// This holds the HRR callback context.
1309
    zero_rtt_check: Option<Pin<Box<ZeroRttCheckState>>>,
1310
}
1311
1312
1.28k
fn load_cert_and_key(name: &str) -> Res<(p11::Certificate, PrivateKey)> {
1313
1.28k
    let c = CString::new(name)?;
1314
1.28k
    let cert = p11::Certificate::from_ptr(unsafe {
1315
1.28k
        p11::PK11_FindCertFromNickname(c.as_ptr(), null_mut())
1316
    })
1317
1.28k
    .map_err(|_| Error::CertificateLoading)?;
1318
1.28k
    let key = PrivateKey::from_ptr(unsafe { p11::PK11_FindKeyByAnyCert(*cert, null_mut()) })
1319
1.28k
        .map_err(|_| Error::CertificateLoading)?;
1320
1.28k
    Ok((cert, key))
1321
1.28k
}
1322
1323
impl Server {
1324
    /// Create a new server agent.
1325
    ///
1326
    /// # Errors
1327
    ///
1328
    /// Errors returned when NSS fails.
1329
1.28k
    pub fn new<A: AsRef<str>>(certificates: &[A]) -> Res<Self> {
1330
1.28k
        let mut agent = SecretAgent::new()?;
1331
1.28k
        for n in certificates {
1332
1.28k
            let (cert, key) = load_cert_and_key(n.as_ref())?;
1333
1.28k
            secstatus_to_res(unsafe {
1334
1.28k
                ssl::SSL_ConfigServerCert(agent.fd, (*cert).cast(), (*key).cast(), null(), 0)
1335
0
            })?;
1336
        }
1337
1.28k
        agent.ready(true, true)?;
1338
1.28k
        Ok(Self {
1339
1.28k
            agent,
1340
1.28k
            zero_rtt_check: None,
1341
1.28k
        })
1342
1.28k
    }
Unexecuted instantiation: <nss_rs::agent::Server>::new::<alloc::string::String>
Unexecuted instantiation: <nss_rs::agent::Server>::new::<&str>
Unexecuted instantiation: <nss_rs::agent::Server>::new::<alloc::string::String>
Unexecuted instantiation: <nss_rs::agent::Server>::new::<alloc::string::String>
Unexecuted instantiation: <nss_rs::agent::Server>::new::<_>
<nss_rs::agent::Server>::new::<&str>
Line
Count
Source
1329
484
    pub fn new<A: AsRef<str>>(certificates: &[A]) -> Res<Self> {
1330
484
        let mut agent = SecretAgent::new()?;
1331
484
        for n in certificates {
1332
484
            let (cert, key) = load_cert_and_key(n.as_ref())?;
1333
484
            secstatus_to_res(unsafe {
1334
484
                ssl::SSL_ConfigServerCert(agent.fd, (*cert).cast(), (*key).cast(), null(), 0)
1335
0
            })?;
1336
        }
1337
484
        agent.ready(true, true)?;
1338
484
        Ok(Self {
1339
484
            agent,
1340
484
            zero_rtt_check: None,
1341
484
        })
1342
484
    }
<nss_rs::agent::Server>::new::<&str>
Line
Count
Source
1329
805
    pub fn new<A: AsRef<str>>(certificates: &[A]) -> Res<Self> {
1330
805
        let mut agent = SecretAgent::new()?;
1331
805
        for n in certificates {
1332
805
            let (cert, key) = load_cert_and_key(n.as_ref())?;
1333
805
            secstatus_to_res(unsafe {
1334
805
                ssl::SSL_ConfigServerCert(agent.fd, (*cert).cast(), (*key).cast(), null(), 0)
1335
0
            })?;
1336
        }
1337
805
        agent.ready(true, true)?;
1338
805
        Ok(Self {
1339
805
            agent,
1340
805
            zero_rtt_check: None,
1341
805
        })
1342
805
    }
1343
1344
    /// Create a server with OCSP responses and SCTs configured.
1345
    /// Not suitable for multiple certificates, because it configures the same OCSP/SCT for all
1346
    /// certificates. In other words, this is good for testing that the plumbing works, not for
1347
    /// a real server.
1348
    ///
1349
    /// # Errors
1350
    ///
1351
    /// Errors returned when NSS fails.
1352
0
    pub fn new_with_ocsp_and_scts<A: AsRef<str>>(
1353
0
        certificates: &[A],
1354
0
        ocsp_responses: &[&[u8]],
1355
0
        scts: &[u8],
1356
0
    ) -> Res<Self> {
1357
0
        let mut agent = SecretAgent::new()?;
1358
0
        for n in certificates {
1359
0
            let (cert, key) = load_cert_and_key(n.as_ref())?;
1360
0
            let ocsp_items: Vec<SECItemBorrowed> = ocsp_responses
1361
0
                .iter()
1362
0
                .map(|b| SECItemBorrowed::wrap(b))
1363
0
                .collect::<Res<_>>()?;
1364
0
            let ocsp_array = SECItemArray {
1365
0
                items: ocsp_items.as_ptr().cast::<SECItem>().cast_mut(),
1366
0
                len: c_uint::try_from(ocsp_items.len())?,
1367
            };
1368
0
            let sct_item = SECItemBorrowed::wrap(scts)?;
1369
0
            let extra = ssl::SSLExtraServerCertDataStr {
1370
0
                // ssl_auth_null means "I don't care what sort of certificate this is".
1371
0
                authType: ssl::SSLAuthType::ssl_auth_null,
1372
0
                certChain: null(),
1373
0
                stapledOCSPResponses: &raw const ocsp_array,
1374
0
                signedCertTimestamps: std::ptr::from_ref(&sct_item).cast(),
1375
0
                delegCred: null(),
1376
0
                delegCredPrivKey: null(),
1377
0
            };
1378
0
            secstatus_to_res(unsafe {
1379
0
                ssl::SSL_ConfigServerCert(
1380
0
                    agent.fd,
1381
0
                    (*cert).cast(),
1382
0
                    (*key).cast(),
1383
0
                    &raw const extra,
1384
0
                    c_uint::try_from(size_of::<ssl::SSLExtraServerCertDataStr>())?,
1385
                )
1386
0
            })?;
1387
        }
1388
0
        agent.ready(true, true)?;
1389
0
        Ok(Self {
1390
0
            agent,
1391
0
            zero_rtt_check: None,
1392
0
        })
1393
0
    }
1394
1395
484
    unsafe extern "C" fn hello_retry_cb(
1396
484
        first_hello: PRBool,
1397
484
        client_token: *const u8,
1398
484
        client_token_len: c_uint,
1399
484
        retry_token: *mut u8,
1400
484
        retry_token_len: *mut c_uint,
1401
484
        retry_token_max: c_uint,
1402
484
        arg: *mut c_void,
1403
484
    ) -> ssl::SSLHelloRetryRequestAction::Type {
1404
484
        if first_hello == 0 {
1405
            // On the second ClientHello after HelloRetryRequest, skip checks.
1406
0
            return ssl::SSLHelloRetryRequestAction::ssl_hello_retry_accept;
1407
484
        }
1408
1409
484
        let check_state = unsafe { arg.cast::<ZeroRttCheckState>().as_mut().unwrap() };
1410
484
        let token =
1411
484
            unsafe { null_safe_slice(client_token, usize::try_from(client_token_len).unwrap()) };
1412
484
        match check_state.checker.check(token) {
1413
0
            ZeroRttCheckResult::Accept => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_accept,
1414
0
            ZeroRttCheckResult::Fail => ssl::SSLHelloRetryRequestAction::ssl_hello_retry_fail,
1415
            ZeroRttCheckResult::Reject => {
1416
484
                ssl::SSLHelloRetryRequestAction::ssl_hello_retry_reject_0rtt
1417
            }
1418
0
            ZeroRttCheckResult::HelloRetryRequest(tok) => {
1419
                // Don't bother propagating errors from this, because it should be caught in
1420
                // testing.
1421
0
                assert!(tok.len() <= usize::try_from(retry_token_max).unwrap());
1422
                // and `retry_token_len` is a valid pointer provided by NSS.
1423
0
                unsafe {
1424
0
                    let slc = slice::from_raw_parts_mut(retry_token, tok.len());
1425
0
                    slc.copy_from_slice(&tok);
1426
0
                    *retry_token_len = c_uint::try_from(tok.len()).unwrap();
1427
0
                }
1428
0
                ssl::SSLHelloRetryRequestAction::ssl_hello_retry_request
1429
            }
1430
        }
1431
484
    }
1432
1433
    /// Enable 0-RTT.  This shadows the function of the same name that can be accessed
1434
    /// via the Deref implementation on Server.
1435
    ///
1436
    /// # Errors
1437
    ///
1438
    /// Returns an error if the underlying NSS functions fail.
1439
1.28k
    pub fn enable_0rtt(
1440
1.28k
        &mut self,
1441
1.28k
        anti_replay: &AntiReplay,
1442
1.28k
        max_early_data: u32,
1443
1.28k
        checker: Box<dyn ZeroRttChecker>,
1444
1.28k
    ) -> Res<()> {
1445
1.28k
        let mut check_state = Box::pin(ZeroRttCheckState::new(checker));
1446
        unsafe {
1447
1.28k
            ssl::SSL_HelloRetryRequestCallback(
1448
1.28k
                self.agent.fd,
1449
1.28k
                Some(Self::hello_retry_cb),
1450
1.28k
                as_c_void(&mut check_state),
1451
            )
1452
0
        }?;
1453
1.28k
        unsafe { ssl::SSL_SetMaxEarlyDataSize(self.agent.fd, max_early_data) }?;
1454
1.28k
        self.zero_rtt_check = Some(check_state);
1455
1.28k
        self.agent.enable_0rtt()?;
1456
1.28k
        anti_replay.config_socket(self.fd)?;
1457
1.28k
        Ok(())
1458
1.28k
    }
1459
1460
    /// Send a session ticket to the client.
1461
    /// This adds |extra| application-specific content into that ticket.
1462
    /// The records that are sent are captured and returned.
1463
    ///
1464
    /// # Errors
1465
    ///
1466
    /// If NSS is unable to send a ticket, or if this agent is incorrectly configured.
1467
0
    pub fn send_ticket(&mut self, now: Instant, extra: &[u8]) -> Res<RecordList> {
1468
0
        self.agent.now.set(now)?;
1469
0
        let records = self.setup_raw()?;
1470
1471
        unsafe {
1472
0
            ssl::SSL_SendSessionTicket(self.fd, extra.as_ptr(), c_uint::try_from(extra.len())?)
1473
0
        }?;
1474
1475
0
        Ok(*Pin::into_inner(records))
1476
0
    }
1477
1478
    /// Enable encrypted client hello (ECH).
1479
    ///
1480
    /// # Errors
1481
    ///
1482
    /// Fails when NSS cannot create a key pair.
1483
0
    pub fn enable_ech(
1484
0
        &mut self,
1485
0
        config: u8,
1486
0
        public_name: &str,
1487
0
        sk: &PrivateKey,
1488
0
        pk: &PublicKey,
1489
0
    ) -> Res<()> {
1490
0
        let cfg = ech::encode_config(config, public_name, pk)?;
1491
0
        debug!("[{self}] Enable ECH for a server: {}", hex_with_len(&cfg));
1492
        unsafe {
1493
0
            ech::SSL_SetServerEchConfigs(
1494
0
                self.agent.fd,
1495
0
                **pk,
1496
0
                **sk,
1497
0
                cfg.as_ptr(),
1498
0
                c_uint::try_from(cfg.len())?,
1499
0
            )?;
1500
        };
1501
0
        self.ech_config = cfg;
1502
0
        Ok(())
1503
0
    }
1504
}
1505
1506
impl Deref for Server {
1507
    type Target = SecretAgent;
1508
5.96k
    fn deref(&self) -> &SecretAgent {
1509
5.96k
        &self.agent
1510
5.96k
    }
1511
}
1512
1513
impl DerefMut for Server {
1514
8.38k
    fn deref_mut(&mut self) -> &mut SecretAgent {
1515
8.38k
        &mut self.agent
1516
8.38k
    }
1517
}
1518
1519
impl Display for Server {
1520
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1521
0
        write!(f, "Server {:p}", self.agent.fd)
1522
0
    }
1523
}
1524
1525
/// A generic container for Client or Server.
1526
#[derive(Debug)]
1527
pub enum Agent {
1528
    Client(Client),
1529
    Server(Server),
1530
}
1531
1532
impl Deref for Agent {
1533
    type Target = SecretAgent;
1534
9.83k
    fn deref(&self) -> &SecretAgent {
1535
9.83k
        match self {
1536
5.15k
            Self::Client(c) => c,
1537
4.67k
            Self::Server(s) => s,
1538
        }
1539
9.83k
    }
1540
}
1541
1542
impl DerefMut for Agent {
1543
16.1k
    fn deref_mut(&mut self) -> &mut SecretAgent {
1544
16.1k
        match self {
1545
7.73k
            Self::Client(c) => c,
1546
8.38k
            Self::Server(s) => s,
1547
        }
1548
16.1k
    }
1549
}
1550
1551
impl From<Client> for Agent {
1552
1.28k
    fn from(c: Client) -> Self {
1553
1.28k
        Self::Client(c)
1554
1.28k
    }
1555
}
1556
1557
impl From<Server> for Agent {
1558
1.28k
    fn from(s: Server) -> Self {
1559
1.28k
        Self::Server(s)
1560
1.28k
    }
1561
}
1562
1563
#[cfg(test)]
1564
#[cfg_attr(coverage_nightly, coverage(off))]
1565
mod tests {
1566
    use crate::ResumptionToken;
1567
1568
    #[test]
1569
    fn resumption_token_debug_impl() {
1570
        let now = test_fixture::now();
1571
        let token = [
1572
            2, 0, 6, 60, 37, 21, 238, 165, 182, 0, 6, 60, 77, 81, 157, 101, 182, 0, 6, 60, 37, 21,
1573
            238, 165, 182, 0, 2, 163, 0, 0, 0, 0, 1, 72, 146, 254, 127, 255, 255, 255, 255, 0, 1,
1574
            73, 48, 130, 1, 69, 48, 129, 236, 160, 3, 2, 1, 2, 2, 5, 0, 176, 13, 245, 143, 48, 10,
1575
            6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 48, 15, 49, 13, 48, 11, 6, 3, 85, 4, 3, 19, 4,
1576
            116, 101, 115, 116, 48, 30, 23, 13, 49, 57, 48, 49, 50, 55, 49, 50, 50, 54, 52, 55, 90,
1577
            23, 13, 49, 57, 48, 52, 50, 55, 49, 50, 50, 54, 52, 55, 90, 48, 15, 49, 13, 48, 11, 6,
1578
            3, 85, 4, 3, 19, 4, 116, 101, 115, 116, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2,
1579
            1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 42, 240, 199, 17, 152, 64, 127,
1580
            175, 32, 106, 156, 147, 147, 171, 185, 13, 157, 177, 225, 249, 112, 141, 249, 175, 72,
1581
            224, 44, 119, 74, 249, 38, 109, 45, 217, 239, 119, 190, 34, 246, 151, 97, 85, 39, 175,
1582
            182, 174, 5, 16, 183, 139, 81, 228, 52, 245, 172, 45, 183, 68, 82, 214, 10, 3, 114, 4,
1583
            163, 53, 48, 51, 48, 25, 6, 3, 85, 29, 17, 4, 18, 48, 16, 130, 14, 115, 101, 114, 118,
1584
            101, 114, 46, 101, 120, 97, 109, 112, 108, 101, 48, 9, 6, 3, 85, 29, 19, 4, 2, 48, 0,
1585
            48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 7, 128, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3,
1586
            2, 3, 72, 0, 48, 69, 2, 32, 118, 227, 238, 3, 17, 159, 222, 78, 215, 173, 203, 63, 51,
1587
            101, 41, 145, 17, 156, 179, 18, 64, 146, 197, 54, 64, 212, 255, 201, 133, 92, 244, 58,
1588
            2, 33, 0, 148, 138, 31, 164, 15, 1, 107, 82, 152, 245, 77, 127, 251, 227, 229, 183, 33,
1589
            162, 111, 169, 222, 240, 171, 167, 99, 81, 10, 183, 76, 80, 130, 65, 0, 0, 0, 5, 91,
1590
            58, 58, 49, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 3,
1591
            4, 0, 6, 60, 37, 21, 238, 165, 182, 0, 4, 0, 0, 1, 0, 0, 8, 0, 0, 0, 255, 0, 17, 236,
1592
            0, 4, 3, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1593
            0, 0, 0, 0, 0, 0, 0, 0, 19, 1, 1, 48, 245, 92, 231, 183, 9, 67, 178, 200, 227, 203, 91,
1594
            5, 146, 135, 61, 159, 135, 68, 96, 200, 86, 35, 189, 174, 81, 95, 157, 75, 177, 235,
1595
            124, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 1,
1596
            50, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2,
1597
            104, 51, 1, 34, 56, 7, 64, 215, 199, 178, 229, 41, 63, 246, 119, 142, 0, 0, 0, 0, 86,
1598
            68, 109, 48, 102, 65, 96, 24, 170, 163, 83, 214, 9, 243, 208, 236, 0, 224, 110, 111,
1599
            243, 244, 243, 4, 172, 188, 247, 135, 255, 109, 241, 240, 77, 178, 162, 32, 255, 45,
1600
            207, 25, 89, 74, 86, 140, 114, 11, 192, 20, 144, 139, 49, 228, 250, 252, 228, 107, 5,
1601
            62, 203, 139, 146, 248, 73, 124, 72, 94, 138, 216, 190, 223, 150, 181, 55, 62, 32, 13,
1602
            231, 230, 104, 142, 27, 182, 15, 13, 116, 26, 234, 154, 228, 241, 134, 131, 25, 214,
1603
            229, 187, 181, 219, 209, 217, 53, 162, 177, 203, 100, 80, 175, 64, 226, 159, 115, 202,
1604
            43, 68, 72, 160, 34, 214, 158, 4, 242, 7, 13, 132, 20, 128, 160, 237, 168, 122, 66, 37,
1605
            54, 11, 116, 148, 94, 173, 80, 228, 7, 89, 223, 156, 249, 22, 50, 62, 33, 22, 157, 115,
1606
            28, 195, 4, 220, 57, 62, 204, 222, 188, 211, 82, 34, 121, 54, 106, 197, 183, 98, 155,
1607
            36, 249, 164, 205, 114, 61, 39, 144, 54, 43, 180, 25, 28, 130, 80, 22, 109, 133, 27,
1608
            87, 184, 66, 54, 117, 151, 113, 36, 172, 98, 31, 60, 42, 85, 190, 148, 160, 65, 222,
1609
            186, 200, 101, 183, 103, 23, 95, 97, 7, 248, 159, 114, 229, 74, 252, 55, 68, 254, 63,
1610
            244, 195, 194, 99, 118, 60, 231, 118, 219, 241, 146, 94, 249, 94, 40, 231, 103, 238,
1611
            215, 120, 60, 213, 142, 121, 11, 15, 251, 195, 120, 39, 17, 117, 183, 1, 90, 178, 149,
1612
            74, 184, 51, 9, 38, 114, 144, 66, 153,
1613
        ];
1614
        let resumption_token = ResumptionToken::new(token.to_vec(), now);
1615
        let expected = format!(
1616
            "ResumptionToken {{ token: \"[848]: 0200063c2515eea5..b833092672904299\", expiration_time: {now:?} }}"
1617
        );
1618
        assert_eq!(format!("{resumption_token:?}"), expected);
1619
    }
1620
}