/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 | | } |