Skip to main content

ntex_tls/x509/
peer.rs

1//! Certificates presented by a TLS peer.
2use ntex_bytes::Bytes;
3
4use crate::x509::{Certificate, Error};
5
6/// Leaf certificate and additional chain certificates presented by a TLS peer.
7///
8/// Built from `PeerCertDer` / `PeerCertChainDer` (moved out of the io query with
9/// `QueryItem::into_inner`). Presence of a chain does **not** imply trust,
10/// use [`crate::x509::trust::CaCerts::is_trusted`].
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct PeerCerts {
13    leaf: Certificate,
14    chain: Vec<Certificate>,
15}
16
17impl PeerCerts {
18    /// Parses the peer leaf certificate and chain, taking ownership of the buffers.
19    ///
20    /// `chain` is expected leaf-first in the order sent by the peer. If its first element is
21    /// the leaf itself (ntex-tls OpenSSL server side) it is removed, so [`Self::chain`]
22    /// never contains the leaf.
23    ///
24    /// # Errors
25    /// [`Error::InvalidDer`] if the leaf or any chain certificate cannot be parsed.
26    pub fn from_der(leaf: Bytes, chain: Vec<Bytes>) -> Result<Self, Error> {
27        let mut chain = chain.into_iter().peekable();
28        if chain.peek().is_some_and(|first| *first == leaf) {
29            chain.next();
30        }
31        let chain = chain
32            .map(Certificate::from_der)
33            .collect::<Result<Vec<_>, _>>()?;
34        Ok(PeerCerts {
35            leaf: Certificate::from_der(leaf)?,
36            chain,
37        })
38    }
39
40    /// Creates peer certificates from already parsed certificates.
41    ///
42    /// `chain` must not contain the leaf, leaf issuer first.
43    #[must_use]
44    pub fn new(leaf: Certificate, chain: Vec<Certificate>) -> Self {
45        PeerCerts { leaf, chain }
46    }
47
48    /// Peer (leaf) certificate.
49    #[must_use]
50    pub fn leaf(&self) -> &Certificate {
51        &self.leaf
52    }
53
54    /// Additional certificates sent by the peer, leaf excluded, leaf issuer first.
55    #[must_use]
56    pub fn chain(&self) -> &[Certificate] {
57        &self.chain
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::x509::testgen::{self, San};
65
66    /// STATEMENT: Peer parsing removes the redundant leaf but retains issuer certificates.
67    ///
68    /// HOW:
69    /// - Parse chains with and without a repeated leaf, an empty chain, and malformed DER.
70    #[test]
71    fn strips_leaf_from_chain() {
72        let k = testgen::key();
73        let san = San::default();
74        let ca = testgen::cert("ca", true, None, &k, &san, (-1, 1));
75        let k2 = testgen::key();
76        let leaf = testgen::cert("leaf", false, Some((&ca, &k)), &k2, &san, (-1, 1));
77        let leaf_der = leaf.der().clone();
78        let ca_der = ca.der().clone();
79
80        let p =
81            PeerCerts::from_der(leaf_der.clone(), vec![leaf_der.clone(), ca_der.clone()]).unwrap();
82        assert_eq!(p.leaf().subject_cn(), Some("leaf"));
83        assert_eq!(p.chain().len(), 1);
84        assert_eq!(p.chain()[0].subject_cn(), Some("ca"));
85
86        let p = PeerCerts::from_der(leaf_der.clone(), vec![ca_der]).unwrap();
87        assert_eq!(p.chain().len(), 1);
88        let p = PeerCerts::from_der(leaf_der.clone(), vec![]).unwrap();
89        assert_eq!(p.chain(), []);
90        assert!(PeerCerts::from_der(leaf_der, vec![Bytes::from_static(b"x")]).is_err());
91    }
92}