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/util.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::{
8
    convert::TryFrom as _, marker::PhantomData, os::raw::c_uint, ptr::null_mut, slice::Iter,
9
};
10
11
use crate::{Res, nss_prelude::*, null_safe_slice};
12
13
/// Implement a smart pointer for NSS objects.
14
///
15
/// Most of the time the pointer is like a `Box`, but there are exceptions (e.g.
16
/// `PK11SymKey` is internally reference counted so its pointer is like an `Arc`.)
17
///
18
/// Named "scoped" because that is what NSS calls its `unique_ptr` typedefs.
19
#[macro_export]
20
macro_rules! scoped_ptr {
21
    ($name:ident, $target:ty, $dtor:path) => {
22
        pub struct $name {
23
            ptr: *mut $target,
24
        }
25
26
        impl $name {
27
            /// Create a new instance of `$name` from a pointer.
28
            ///
29
            /// # Errors
30
            /// When passed a null pointer generates an error.
31
128k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
128k
                let ptr = $crate::err::into_result(raw)?;
33
128k
                Ok(Self { ptr })
34
128k
            }
<nss_rs::p11::Certificate>::from_ptr
Line
Count
Source
31
1.28k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
1.28k
                let ptr = $crate::err::into_result(raw)?;
33
1.28k
                Ok(Self { ptr })
34
1.28k
            }
<nss_rs::p11::Context>::from_ptr
Line
Count
Source
31
32.4k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
32.4k
                let ptr = $crate::err::into_result(raw)?;
33
32.4k
                Ok(Self { ptr })
34
32.4k
            }
<nss_rs::replay::AntiReplayContext>::from_ptr
Line
Count
Source
31
1.28k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
1.28k
                let ptr = $crate::err::into_result(raw)?;
33
1.28k
                Ok(Self { ptr })
34
1.28k
            }
Unexecuted instantiation: <nss_rs::util::ScopedSECItem>::from_ptr
Unexecuted instantiation: <nss_rs::p11::CertList>::from_ptr
Unexecuted instantiation: <nss_rs::util::ScopedSECItemArray>::from_ptr
Unexecuted instantiation: <nss_rs::p11::SubjectPublicKeyInfo>::from_ptr
Unexecuted instantiation: <nss_rs::p11::PublicKey>::from_ptr
<nss_rs::p11::PrivateKey>::from_ptr
Line
Count
Source
31
1.28k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
1.28k
                let ptr = $crate::err::into_result(raw)?;
33
1.28k
                Ok(Self { ptr })
34
1.28k
            }
<nss_rs::p11::Slot>::from_ptr
Line
Count
Source
31
19.2k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
19.2k
                let ptr = $crate::err::into_result(raw)?;
33
19.2k
                Ok(Self { ptr })
34
19.2k
            }
<nss_rs::p11::SymKey>::from_ptr
Line
Count
Source
31
72.7k
            pub fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
32
72.7k
                let ptr = $crate::err::into_result(raw)?;
33
72.7k
                Ok(Self { ptr })
34
72.7k
            }
35
        }
36
37
        impl $crate::err::IntoResult for *mut $target {
38
            type Ok = $name;
39
40
0
            fn into_result(self) -> Result<Self::Ok, $crate::err::Error> {
41
0
                $name::from_ptr(self)
42
0
            }
Unexecuted instantiation: <*mut nss_rs::replay::SSLAntiReplayContext as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::CERTCertificateStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::PK11ContextStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::nss_prelude::SECItemStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::CERTCertListStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::nss_prelude::SECItemArrayStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::CERTSubjectPublicKeyInfoStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::SECKEYPublicKeyStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::SECKEYPrivateKeyStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::PK11SlotInfoStr as nss_rs::err::IntoResult>::into_result
Unexecuted instantiation: <*mut nss_rs::p11::nss_p11::PK11SymKeyStr as nss_rs::err::IntoResult>::into_result
43
        }
44
45
        impl std::ops::Deref for $name {
46
            type Target = *mut $target;
47
48
150k
            fn deref(&self) -> &*mut $target {
49
150k
                &self.ptr
50
150k
            }
<nss_rs::p11::Certificate as core::ops::deref::Deref>::deref
Line
Count
Source
48
2.57k
            fn deref(&self) -> &*mut $target {
49
2.57k
                &self.ptr
50
2.57k
            }
<nss_rs::p11::PrivateKey as core::ops::deref::Deref>::deref
Line
Count
Source
48
1.28k
            fn deref(&self) -> &*mut $target {
49
1.28k
                &self.ptr
50
1.28k
            }
<nss_rs::replay::AntiReplayContext as core::ops::deref::Deref>::deref
Line
Count
Source
48
1.28k
            fn deref(&self) -> &*mut $target {
49
1.28k
                &self.ptr
50
1.28k
            }
<nss_rs::p11::SymKey as core::ops::deref::Deref>::deref
Line
Count
Source
48
117k
            fn deref(&self) -> &*mut $target {
49
117k
                &self.ptr
50
117k
            }
<nss_rs::p11::Context as core::ops::deref::Deref>::deref
Line
Count
Source
48
8.76k
            fn deref(&self) -> &*mut $target {
49
8.76k
                &self.ptr
50
8.76k
            }
Unexecuted instantiation: <nss_rs::p11::SubjectPublicKeyInfo as core::ops::deref::Deref>::deref
Unexecuted instantiation: <nss_rs::p11::PublicKey as core::ops::deref::Deref>::deref
<nss_rs::p11::Slot as core::ops::deref::Deref>::deref
Line
Count
Source
48
19.2k
            fn deref(&self) -> &*mut $target {
49
19.2k
                &self.ptr
50
19.2k
            }
Unexecuted instantiation: <nss_rs::util::ScopedSECItem as core::ops::deref::Deref>::deref
Unexecuted instantiation: <nss_rs::p11::CertList as core::ops::deref::Deref>::deref
Unexecuted instantiation: <nss_rs::util::ScopedSECItemArray as core::ops::deref::Deref>::deref
51
        }
52
53
        // Original implements DerefMut, but is that really a good idea?
54
55
        impl Drop for $name {
56
150k
            fn drop(&mut self) {
57
150k
                unsafe { _ = $dtor(self.ptr) };
58
150k
            }
<nss_rs::replay::AntiReplayContext as core::ops::drop::Drop>::drop
Line
Count
Source
56
1.28k
            fn drop(&mut self) {
57
1.28k
                unsafe { _ = $dtor(self.ptr) };
58
1.28k
            }
<nss_rs::p11::SymKey as core::ops::drop::Drop>::drop
Line
Count
Source
56
94.8k
            fn drop(&mut self) {
57
94.8k
                unsafe { _ = $dtor(self.ptr) };
58
94.8k
            }
<nss_rs::p11::Context as core::ops::drop::Drop>::drop
Line
Count
Source
56
32.4k
            fn drop(&mut self) {
57
32.4k
                unsafe { _ = $dtor(self.ptr) };
58
32.4k
            }
<nss_rs::p11::Certificate as core::ops::drop::Drop>::drop
Line
Count
Source
56
1.28k
            fn drop(&mut self) {
57
1.28k
                unsafe { _ = $dtor(self.ptr) };
58
1.28k
            }
Unexecuted instantiation: <nss_rs::util::ScopedSECItem as core::ops::drop::Drop>::drop
Unexecuted instantiation: <nss_rs::p11::CertList as core::ops::drop::Drop>::drop
Unexecuted instantiation: <nss_rs::util::ScopedSECItemArray as core::ops::drop::Drop>::drop
Unexecuted instantiation: <nss_rs::p11::SubjectPublicKeyInfo as core::ops::drop::Drop>::drop
Unexecuted instantiation: <nss_rs::p11::PublicKey as core::ops::drop::Drop>::drop
<nss_rs::p11::PrivateKey as core::ops::drop::Drop>::drop
Line
Count
Source
56
1.28k
            fn drop(&mut self) {
57
1.28k
                unsafe { _ = $dtor(self.ptr) };
58
1.28k
            }
<nss_rs::p11::Slot as core::ops::drop::Drop>::drop
Line
Count
Source
56
19.2k
            fn drop(&mut self) {
57
19.2k
                unsafe { _ = $dtor(self.ptr) };
58
19.2k
            }
59
        }
60
    };
61
}
62
63
macro_rules! impl_clone {
64
    ($name:ty, $nss_fn:path) => {
65
        impl Clone for $name {
66
0
            fn clone(&self) -> Self {
67
0
                let ptr = unsafe { $nss_fn(self.ptr) };
68
0
                assert!(!ptr.is_null());
69
0
                Self { ptr }
70
0
            }
Unexecuted instantiation: <nss_rs::p11::SymKey as core::clone::Clone>::clone
Unexecuted instantiation: <nss_rs::p11::PublicKey as core::clone::Clone>::clone
Unexecuted instantiation: <nss_rs::p11::PrivateKey as core::clone::Clone>::clone
71
        }
72
    };
73
}
74
75
impl SECItem {
76
    /// Return contents as a slice.
77
    ///
78
    /// Unsafe due to calling `from_raw_parts`, or if 'a outlives &self. This
79
    /// unsafety is encapsulated by the `as_slice` method of `SECItemBorrowed`
80
    /// and `SECItemMut`.
81
    ///
82
    /// Note that safe code can construct a `SECItem` pointing to anything. The
83
    /// same is not true of the safe wrappers `SECItemMut` and `SECItemBorrowed`
84
    /// because their inner `SECItem` is private.
85
    #[must_use]
86
0
    pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
87
        // Sanity check the type, as some types don't count bytes in `Item::len`.
88
0
        assert_eq!(self.type_, SECItemType::siBuffer);
89
        // Note: `from_raw_parts` requires non-null `data` even for zero-length
90
        // slices.
91
0
        if self.len != 0 {
92
            unsafe {
93
0
                null_safe_slice(
94
0
                    self.data,
95
0
                    usize::try_from(self.len).expect("Buffer too long"),
96
0
                )
97
            }
98
        } else {
99
0
            &[]
100
        }
101
0
    }
102
}
103
104
0
unsafe fn destroy_secitem(item: *mut SECItem) {
105
0
    unsafe {
106
0
        SECITEM_FreeItem(item, PRBool::from(true));
107
0
    }
108
0
}
109
scoped_ptr!(ScopedSECItem, SECItem, destroy_secitem);
110
111
impl ScopedSECItem {
112
    /// This dereferences the pointer held by the item and makes a copy of the
113
    /// content that is referenced there.
114
    ///
115
    /// # Safety
116
    /// This dereferences two pointers.  It doesn't get much less safe.
117
    #[must_use]
118
0
    pub unsafe fn into_vec(self) -> Vec<u8> {
119
0
        let b = unsafe { self.ptr.as_ref().expect("Null pointer") };
120
        // Sanity check the type, as some types don't count bytes in `Item::len`.
121
0
        assert_eq!(b.type_, SECItemType::siBuffer);
122
0
        let slc =
123
0
            unsafe { null_safe_slice(b.data, usize::try_from(b.len).expect("Buffer too long")) };
124
0
        Vec::from(slc)
125
0
    }
126
}
127
128
0
unsafe fn destroy_secitem_array(array: *mut SECItemArray) {
129
0
    unsafe {
130
0
        SECITEM_FreeArray(array, PRBool::from(true));
131
0
    }
132
0
}
133
scoped_ptr!(ScopedSECItemArray, SECItemArray, destroy_secitem_array);
134
135
#[expect(clippy::into_iter_without_iter)]
136
impl<'a> IntoIterator for &'a ScopedSECItemArray {
137
    type Item = &'a [u8];
138
    type IntoIter = ScopedSECItemArrayIterator<'a>;
139
0
    fn into_iter(self) -> Self::IntoIter {
140
0
        Self::IntoIter {
141
0
            iter: AsRef::<[SECItem]>::as_ref(self).iter(),
142
0
        }
143
0
    }
144
}
145
146
impl AsRef<[SECItem]> for ScopedSECItemArray {
147
0
    fn as_ref(&self) -> &[SECItem] {
148
0
        unsafe { null_safe_slice((*self.ptr).items, (*self.ptr).len) }
149
0
    }
150
}
151
152
pub struct ScopedSECItemArrayIterator<'a> {
153
    iter: Iter<'a, SECItem>,
154
}
155
156
impl<'a> Iterator for ScopedSECItemArrayIterator<'a> {
157
    type Item = &'a [u8];
158
0
    fn next(&mut self) -> Option<&'a [u8]> {
159
0
        let item = self.iter.next()?;
160
0
        unsafe { Some(item.as_slice()) }
161
0
    }
162
}
163
164
/// An owned `SECItem`.
165
///
166
/// The `SECItem` structure is allocated by Rust. The buffer referenced by the
167
/// `SECItem` is allocated by NSS. `SECITEM_FreeItem` will be called to free the
168
/// buffer when the `SECItemMut` is dropped.
169
///
170
/// This is used with NSS functions that return a variable amount of data.
171
#[repr(transparent)]
172
pub struct SECItemMut {
173
    inner: SECItem,
174
}
175
176
impl Drop for SECItemMut {
177
0
    fn drop(&mut self) {
178
        // FreeItem unconditionally frees the buffer referenced by the SECItem.
179
        // If the second argument is true, it also frees the SECItem itself,
180
        // which we don't want to do, because rust owns that memory.
181
0
        unsafe {
182
0
            SECITEM_FreeItem(&raw mut self.inner, PRBool::from(false));
183
0
        }
184
0
    }
185
}
186
187
impl AsRef<SECItem> for SECItemMut {
188
0
    fn as_ref(&self) -> &SECItem {
189
0
        &self.inner
190
0
    }
191
}
192
193
impl AsMut<SECItem> for SECItemMut {
194
0
    fn as_mut(&mut self) -> &mut SECItem {
195
0
        &mut self.inner
196
0
    }
197
}
198
199
impl SECItemMut {
200
    /// Return contents as a slice.
201
    #[must_use]
202
0
    pub fn as_slice(&self) -> &[u8] {
203
0
        unsafe { self.inner.as_slice() }
204
0
    }
205
206
    /// Make an empty `SECItemMut` for passing as a mutable `*SECItem` argument.
207
    #[must_use]
208
0
    pub const fn make_empty() -> Self {
209
0
        Self {
210
0
            inner: SECItem {
211
0
                type_: SECItemType::siBuffer,
212
0
                data: null_mut(),
213
0
                len: 0,
214
0
            },
215
0
        }
216
0
    }
217
}
218
219
/// A borrowed `SECItem`.
220
///
221
/// The `SECItem` structure is allocated by Rust. The buffer referenced by the
222
/// `SECItem` may be allocated either by Rust or NSS. The `SECItem` does not own the
223
/// buffer and will not free it when dropped.
224
///
225
/// This is usually used to pass a reference to some borrowed rust memory to
226
/// NSS. It is occasionally used to accept non-owned output data from NSS.
227
#[repr(transparent)]
228
pub struct SECItemBorrowed<'a> {
229
    inner: SECItem,
230
    phantom_data: PhantomData<&'a u8>,
231
}
232
233
impl AsRef<SECItem> for SECItemBorrowed<'_> {
234
32.4k
    fn as_ref(&self) -> &SECItem {
235
32.4k
        &self.inner
236
32.4k
    }
237
}
238
239
impl AsMut<SECItem> for SECItemBorrowed<'_> {
240
    /// Get a mutable reference to the underlying `SECItem` struct.
241
    ///
242
    /// Note that even if the `SECItem` struct is mutable, the buffer it
243
    /// references may not be. Take care not to pass the mutable
244
    /// `SECItem` to NSS routines that will violate mutability rules.
245
    //
246
    // TODO: Should we make the danger more obvious, by using a non-trait method
247
    // with "unsafe" in the name, or an unsafe method?
248
19.2k
    fn as_mut(&mut self) -> &mut SECItem {
249
19.2k
        &mut self.inner
250
19.2k
    }
251
}
252
253
impl<'a> SECItemBorrowed<'a> {
254
    /// Return contents as a slice.
255
    #[must_use]
256
0
    pub fn as_slice(&self) -> &'a [u8] {
257
0
        unsafe { self.inner.as_slice() }
258
0
    }
259
260
    /// Create an empty `SECItemBorrowed`.
261
    ///
262
    /// This can be used (1) to pass an empty item as an argument, and (2) as an
263
    /// output parameter when NSS returns a pointer to NSS-owned memory that
264
    /// should not be freed when the `SECItem` is dropped.  If the memory should
265
    /// be freed when the `SECItem` is dropped, use `SECItemMut`.
266
    ///
267
    /// It is safe to let the caller specify any lifetime here because no
268
    /// borrowing is actually taking place. However, if the pointer in the
269
    /// returned item is modified, care must be taken that the specified
270
    /// lifetime accurately reflects the data referenced by the pointer.
271
    #[must_use]
272
10.6k
    pub const fn make_empty() -> Self {
273
10.6k
        SECItemBorrowed {
274
10.6k
            inner: SECItem {
275
10.6k
                type_: SECItemType::siBuffer,
276
10.6k
                data: null_mut(),
277
10.6k
                len: 0,
278
10.6k
            },
279
10.6k
            phantom_data: PhantomData,
280
10.6k
        }
281
10.6k
    }
282
283
    /// Create a `SECItemBorrowed` wrapping a slice.
284
    ///
285
    /// Creating this object is technically safe, but using it is extremely dangerous.
286
    /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
287
    /// or those that treat their argument as `const`.
288
41.0k
    pub fn wrap(buf: &'a [u8]) -> Res<Self> {
289
        Ok(Self {
290
            inner: SECItem {
291
                type_: SECItemType::siBuffer,
292
41.0k
                data: buf.as_ptr().cast_mut(),
293
41.0k
                len: c_uint::try_from(buf.len())?,
294
            },
295
41.0k
            phantom_data: PhantomData,
296
        })
297
41.0k
    }
298
299
    /// Create a `SECItemBorrowed` wrapping a struct.
300
    ///
301
    /// Creating this object is technically safe, but using it is extremely dangerous.
302
    /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
303
    /// or those that treat their argument as `const`.
304
0
    pub fn wrap_struct<T>(v: &'a T) -> Res<Self> {
305
0
        let data: *const T = v;
306
        Ok(Self {
307
            inner: SECItem {
308
                type_: SECItemType::siBuffer,
309
0
                data: data.cast_mut().cast(),
310
0
                len: c_uint::try_from(size_of::<T>())?,
311
            },
312
0
            phantom_data: PhantomData,
313
        })
314
0
    }
315
}