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/cert.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
use std::{convert::TryFrom as _, ptr::NonNull};
8
9
use log::error;
10
11
use crate::{
12
    SECItem, SECItemArray, ScopedSECItemArray, ScopedSECItemArrayIterator, experimental_api,
13
    null_safe_slice,
14
    prio::PRFileDesc,
15
    ssl::{SSL_PeerSignedCertTimestamps, SSL_PeerStapledOCSPResponses},
16
};
17
18
experimental_api!(SSL_PeerCertificateChainDER(
19
    fd: *mut PRFileDesc,
20
    out: *mut *mut SECItemArray,
21
));
22
23
pub struct CertificateInfo {
24
    certs: ScopedSECItemArray,
25
    /// `stapled_ocsp_responses` and `signed_cert_timestamp` are properties
26
    /// associated with each of the certificates. Right now, NSS only
27
    /// reports the value for the end-entity certificate (the first).
28
    stapled_ocsp_responses: Option<Vec<Vec<u8>>>,
29
    signed_cert_timestamp: Option<Vec<u8>>,
30
}
31
32
0
fn peer_certificate_chain(fd: *mut PRFileDesc) -> Option<ScopedSECItemArray> {
33
0
    let mut chain_ptr: *mut SECItemArray = std::ptr::null_mut();
34
0
    let rv = unsafe { SSL_PeerCertificateChainDER(fd, &raw mut chain_ptr) };
35
0
    if rv.is_ok() {
36
0
        ScopedSECItemArray::from_ptr(chain_ptr).ok()
37
    } else {
38
0
        None
39
    }
40
0
}
41
42
// As explained in rfc6961, an OCSPResponseList can have at most
43
// 2^24 items. Casting its length is therefore safe even on 32 bits targets.
44
0
fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> {
45
0
    let ocsp_nss = unsafe { SSL_PeerStapledOCSPResponses(fd) };
46
0
    let ocsp_ptr = NonNull::new(ocsp_nss.cast_mut())?;
47
0
    let Ok(len) = usize::try_from(unsafe { ocsp_ptr.as_ref().len }) else {
48
0
        error!("[{fd:p}] Received illegal OCSP length");
49
0
        return None;
50
    };
51
    Some(
52
0
        (0..len)
53
0
            .map(|idx| {
54
0
                let itemp: *const SECItem = unsafe { ocsp_ptr.as_ref().items.add(idx).cast() };
55
0
                unsafe { null_safe_slice((*itemp).data, (*itemp).len) }.to_owned()
56
0
            })
57
0
            .collect(),
58
    )
59
0
}
60
61
0
fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
62
0
    let sct_nss = unsafe { SSL_PeerSignedCertTimestamps(fd) };
63
0
    NonNull::new(sct_nss.cast_mut()).map(|sct_ptr| {
64
0
        if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } {
65
0
            Vec::new()
66
        } else {
67
0
            let sct_slice = unsafe { null_safe_slice(sct_ptr.as_ref().data, sct_ptr.as_ref().len) };
68
0
            sct_slice.to_owned()
69
        }
70
0
    })
71
0
}
72
73
impl<'a> IntoIterator for &'a CertificateInfo {
74
    type IntoIter = ScopedSECItemArrayIterator<'a>;
75
    type Item = &'a [u8];
76
0
    fn into_iter(self) -> Self::IntoIter {
77
0
        self.iter()
78
0
    }
79
}
80
81
impl CertificateInfo {
82
0
    pub(crate) fn new(fd: *mut PRFileDesc) -> Option<Self> {
83
0
        peer_certificate_chain(fd).map(|certs| Self {
84
0
            certs,
85
0
            stapled_ocsp_responses: stapled_ocsp_responses(fd),
86
0
            signed_cert_timestamp: signed_cert_timestamp(fd),
87
0
        })
88
0
    }
89
90
    #[must_use]
91
0
    pub fn iter(&self) -> ScopedSECItemArrayIterator<'_> {
92
0
        self.certs.into_iter()
93
0
    }
94
95
    #[must_use]
96
0
    pub fn stapled_ocsp_responses(&self) -> Option<&[Vec<u8>]> {
97
0
        self.stapled_ocsp_responses.as_deref()
98
0
    }
99
100
    #[must_use]
101
0
    pub fn signed_cert_timestamp(&self) -> Option<&[u8]> {
102
0
        self.signed_cert_timestamp.as_deref()
103
0
    }
104
}