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/p11.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
#![allow(
8
    dead_code,
9
    non_upper_case_globals,
10
    non_camel_case_types,
11
    non_snake_case,
12
    clippy::unwrap_used
13
)]
14
use std::{
15
    cell::RefCell,
16
    convert::TryFrom as _,
17
    fmt::{self, Debug, Formatter},
18
    os::raw::{c_int, c_uint},
19
    ptr::null_mut,
20
};
21
22
use pkcs11_bindings::{CKA_EC_POINT, CKA_VALUE};
23
24
use crate::{
25
    err::{Error, Res, secstatus_to_res},
26
    nss_prelude::SECITEM_FreeItem,
27
    util::SECItemMut,
28
};
29
30
#[must_use]
31
0
pub fn hex_with_len<B: AsRef<[u8]>>(buf: B) -> String {
32
    use std::fmt::Write as _;
33
0
    let buf = buf.as_ref();
34
0
    let mut ret = String::with_capacity(10 + buf.len() * 2);
35
0
    write!(&mut ret, "[{}]: ", buf.len()).unwrap();
36
0
    for b in buf {
37
0
        write!(&mut ret, "{b:02x}").unwrap();
38
0
    }
39
0
    ret
40
0
}
Unexecuted instantiation: nss_rs::p11::hex_with_len::<alloc::vec::Vec<u8>>
Unexecuted instantiation: nss_rs::p11::hex_with_len::<&alloc::vec::Vec<u8>>
Unexecuted instantiation: nss_rs::p11::hex_with_len::<&[u8]>
41
42
mod nss_p11 {
43
    #![allow(
44
        non_snake_case,
45
        non_upper_case_globals,
46
        non_camel_case_types,
47
        unsafe_op_in_unsafe_fn,
48
        unused_qualifications,
49
        clippy::all,
50
        clippy::nursery,
51
        clippy::pedantic,
52
        clippy::restriction,
53
        reason = "For included bindgen code."
54
    )]
55
    use crate::nss_prelude::*;
56
    include!(concat!(env!("OUT_DIR"), "/nss_p11.rs"));
57
}
58
59
pub use nss_p11::*;
60
61
use crate::null_safe_slice;
62
63
scoped_ptr!(Certificate, CERTCertificate, CERT_DestroyCertificate);
64
scoped_ptr!(CertList, CERTCertList, CERT_DestroyCertList);
65
66
scoped_ptr!(
67
    SubjectPublicKeyInfo,
68
    CERTSubjectPublicKeyInfo,
69
    SECKEY_DestroySubjectPublicKeyInfo
70
);
71
72
scoped_ptr!(PublicKey, SECKEYPublicKey, SECKEY_DestroyPublicKey);
73
impl_clone!(PublicKey, SECKEY_CopyPublicKey);
74
75
impl PublicKey {
76
    /// Get the HPKE serialization of the public key.
77
    ///
78
    /// # Errors
79
    ///
80
    /// When the key cannot be exported, which can be because the type is not supported.
81
    ///
82
    /// # Panics
83
    ///
84
    /// When keys are too large to fit in `c_uint/usize`.  So only on programming error.
85
0
    pub fn key_data(&self) -> Res<Vec<u8>> {
86
0
        let mut buf = vec![0; 100];
87
0
        let mut len: c_uint = 0;
88
0
        secstatus_to_res(unsafe {
89
0
            PK11_HPKE_Serialize(
90
0
                **self,
91
0
                buf.as_mut_ptr(),
92
0
                &raw mut len,
93
0
                c_uint::try_from(buf.len()).map_err(|_| Error::IntegerOverflow)?,
94
            )
95
0
        })?;
96
0
        buf.truncate(usize::try_from(len).map_err(|_| Error::IntegerOverflow)?);
97
0
        Ok(buf)
98
0
    }
99
100
0
    pub fn key_data_alt(&self) -> Res<Vec<u8>> {
101
0
        let mut key_item = SECItemMut::make_empty();
102
0
        secstatus_to_res(unsafe {
103
0
            PK11_ReadRawAttribute(
104
                PK11ObjectType::PK11_TypePubKey,
105
0
                (**self).cast(),
106
                CKA_EC_POINT,
107
0
                key_item.as_mut(),
108
            )
109
0
        })?;
110
0
        Ok(key_item.as_slice().to_owned())
111
0
    }
112
}
113
114
impl Debug for PublicKey {
115
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
116
0
        if let Ok(b) = self.key_data() {
117
0
            write!(f, "PublicKey {}", hex_with_len(b))
118
        } else {
119
0
            write!(f, "Opaque PublicKey")
120
        }
121
0
    }
122
}
123
124
scoped_ptr!(PrivateKey, SECKEYPrivateKey, SECKEY_DestroyPrivateKey);
125
impl_clone!(PrivateKey, SECKEY_CopyPrivateKey);
126
127
impl PrivateKey {
128
    /// Get the bits of the private key.
129
    ///
130
    /// # Errors
131
    ///
132
    /// When the key cannot be exported, which can be because the type is not supported
133
    /// or because the key data cannot be extracted from the PKCS#11 module.
134
    ///
135
    /// # Panics
136
    ///
137
    /// When the values are too large to fit.  So never.
138
0
    pub fn key_data(&self) -> Res<Vec<u8>> {
139
0
        let mut key_item = SECItemMut::make_empty();
140
0
        secstatus_to_res(unsafe {
141
0
            PK11_ReadRawAttribute(
142
                PK11ObjectType::PK11_TypePrivKey,
143
0
                (**self).cast(),
144
                CKA_VALUE,
145
0
                key_item.as_mut(),
146
            )
147
0
        })?;
148
0
        let slc = unsafe { null_safe_slice(key_item.as_ref().data, key_item.as_ref().len) };
149
0
        let key = Vec::from(slc);
150
        // The data that `key_item` refers to needs to be freed, but we can't
151
        // use the scoped `Item` implementation.  This is OK as long as nothing
152
        // panics between `PK11_ReadRawAttribute` succeeding and here.
153
0
        unsafe {
154
0
            SECITEM_FreeItem(key_item.as_mut(), PRBool::from(false));
155
0
        }
156
0
        Ok(key)
157
0
    }
158
}
159
unsafe impl Send for PrivateKey {}
160
161
impl Debug for PrivateKey {
162
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
163
0
        if let Ok(b) = self.key_data() {
164
0
            write!(f, "PrivateKey {}", hex_with_len(b))
165
        } else {
166
0
            write!(f, "Opaque PrivateKey")
167
        }
168
0
    }
169
}
170
171
scoped_ptr!(Slot, PK11SlotInfo, PK11_FreeSlot);
172
173
impl Slot {
174
19.2k
    pub fn internal() -> Res<Self> {
175
19.2k
        unsafe { Self::from_ptr(PK11_GetInternalSlot()) }
176
19.2k
    }
177
178
0
    pub fn internal_key_slot() -> Res<Self> {
179
0
        unsafe { Self::from_ptr(PK11_GetInternalKeySlot()) }
180
0
    }
181
182
    #[must_use]
183
0
    pub fn token_name(&self) -> String {
184
0
        let name = unsafe { PK11_GetTokenName(self.ptr) };
185
0
        if name.is_null() {
186
0
            return String::new();
187
0
        }
188
0
        unsafe { std::ffi::CStr::from_ptr(name) }
189
0
            .to_string_lossy()
190
0
            .into_owned()
191
0
    }
192
193
0
    pub fn authenticate(&self) -> Res<()> {
194
0
        secstatus_to_res(unsafe { PK11_Authenticate(self.ptr, PRBool::from(true), null_mut()) })
195
0
    }
196
197
    /// Check a user-supplied password against this slot.
198
    ///
199
    /// **Note:** This internally logs out before re-authenticating.
200
    /// A failed check leaves the slot in a logged-out state.
201
0
    pub fn check_user_password(&self, password: &str) -> Res<()> {
202
0
        let c_password = std::ffi::CString::new(password)?;
203
0
        secstatus_to_res(unsafe { PK11_CheckUserPassword(self.ptr, c_password.as_ptr()) })
204
0
    }
205
206
0
    pub fn logout(&self) -> Res<()> {
207
0
        secstatus_to_res(unsafe { PK11_Logout(self.ptr) })
208
0
    }
209
210
    /// Find a persistent symmetric key on this slot by nickname.
211
    /// Returns `None` if no key with the given nickname exists.
212
    #[must_use]
213
0
    pub fn find_key_by_nickname(&self, nickname: &str) -> Option<SymKey> {
214
0
        let c_nickname = std::ffi::CString::new(nickname).ok()?;
215
0
        let ptr = unsafe {
216
0
            PK11_ListFixedKeysInSlot(self.ptr, c_nickname.as_ptr().cast_mut(), null_mut())
217
        };
218
0
        if ptr.is_null() {
219
0
            None
220
        } else {
221
0
            SymKey::from_ptr(ptr).ok()
222
        }
223
0
    }
224
225
    /// Generate a persistent symmetric key on this slot with a nickname.
226
0
    pub fn generate_token_key(
227
0
        &self,
228
0
        mechanism: CK_MECHANISM_TYPE,
229
0
        key_size: usize,
230
0
        nickname: &str,
231
0
    ) -> Res<SymKey> {
232
0
        let key = unsafe {
233
0
            SymKey::from_ptr(PK11_TokenKeyGenWithFlags(
234
0
                self.ptr,
235
0
                mechanism,
236
0
                null_mut(),
237
0
                c_int::try_from(key_size).map_err(|_| Error::IntegerOverflow)?,
238
0
                null_mut(),
239
0
                CK_FLAGS::from(CKF_ENCRYPT | CKF_DECRYPT),
240
0
                PK11AttrFlags::from(PK11_ATTR_TOKEN | PK11_ATTR_PRIVATE | PK11_ATTR_SENSITIVE),
241
0
                null_mut(),
242
            ))
243
0
        }?;
244
0
        let c_nickname = std::ffi::CString::new(nickname).map_err(|_| Error::InvalidInput)?;
245
0
        secstatus_to_res(unsafe { PK11_SetSymKeyNickname(*key, c_nickname.as_ptr()) })?;
246
0
        Ok(key)
247
0
    }
248
}
249
250
/// Returns all available token slots for the given mechanism.
251
#[must_use]
252
0
pub fn all_token_slots(mechanism: CK_MECHANISM_TYPE) -> Vec<Slot> {
253
0
    let list = unsafe {
254
0
        PK11_GetAllTokens(
255
0
            mechanism,
256
0
            PRBool::from(false),
257
0
            PRBool::from(false),
258
0
            null_mut(),
259
        )
260
    };
261
0
    if list.is_null() {
262
0
        return Vec::new();
263
0
    }
264
0
    let mut result = Vec::new();
265
    unsafe {
266
0
        let mut elem = (*list).head;
267
0
        while !elem.is_null() {
268
0
            let slot_ptr = (*elem).slot;
269
0
            if !slot_ptr.is_null() {
270
0
                PK11_ReferenceSlot(slot_ptr);
271
0
                if let Ok(slot) = Slot::from_ptr(slot_ptr) {
272
0
                    result.push(slot);
273
0
                }
274
0
            }
275
0
            elem = (*elem).next;
276
        }
277
0
        PK11_FreeSlotList(list);
278
    }
279
0
    result
280
0
}
281
282
// Note: PK11SymKey is internally reference counted
283
scoped_ptr!(SymKey, PK11SymKey, PK11_FreeSymKey);
284
impl_clone!(SymKey, PK11_ReferenceSymKey);
285
286
impl SymKey {
287
    /// You really don't want to use this.
288
    ///
289
    /// # Errors
290
    ///
291
    /// Internal errors in case of failures in NSS.
292
10.9k
    pub fn key_data(&self) -> Res<&[u8]> {
293
10.9k
        secstatus_to_res(unsafe { PK11_ExtractKeyValue(**self) })?;
294
295
10.9k
        let key_item = unsafe { PK11_GetKeyData(**self) };
296
        // This is accessing a value attached to the key, so we can treat this as a borrow.
297
10.9k
        match unsafe { key_item.as_mut() } {
298
0
            None => Err(Error::Internal),
299
10.9k
            Some(key) => Ok(unsafe { null_safe_slice(key.data, key.len) }),
300
        }
301
10.9k
    }
302
303
0
    pub fn as_bytes(&self) -> Res<&[u8]> {
304
0
        self.key_data()
305
0
    }
306
}
307
308
impl Debug for SymKey {
309
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
310
0
        if let Ok(b) = self.key_data() {
311
0
            write!(f, "SymKey {}", hex_with_len(b))
312
        } else {
313
0
            write!(f, "Opaque SymKey")
314
        }
315
0
    }
316
}
317
318
impl Default for SymKey {
319
22.0k
    fn default() -> Self {
320
22.0k
        Self { ptr: null_mut() }
321
22.0k
    }
322
}
323
324
32.4k
unsafe fn destroy_pk11_context(ctxt: *mut PK11Context) {
325
32.4k
    unsafe {
326
32.4k
        PK11_DestroyContext(ctxt, PRBool::from(true));
327
32.4k
    }
328
32.4k
}
329
scoped_ptr!(Context, PK11Context, destroy_pk11_context);
330
331
#[cfg(feature = "disable-random")]
332
thread_local! {
333
    static CURRENT_VALUE: std::cell::Cell<u8> = const { std::cell::Cell::new(0) };
334
}
335
336
#[cfg(feature = "disable-random")]
337
/// Fill a buffer with a predictable sequence of bytes.
338
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
339
    let m_buf = buf.as_mut();
340
    for v in m_buf.iter_mut() {
341
        *v = CURRENT_VALUE.get();
342
        CURRENT_VALUE.set(v.wrapping_add(1));
343
    }
344
    buf
345
}
346
347
/// Fill a buffer with randomness.
348
///
349
/// # Panics
350
///
351
/// When `size` is too large or NSS fails.
352
#[cfg(not(feature = "disable-random"))]
353
1.91k
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
354
1.91k
    let m_buf = buf.as_mut();
355
1.91k
    let len = c_int::try_from(m_buf.len()).expect("usize fits into c_int");
356
1.91k
    secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
357
1.91k
    buf
358
1.91k
}
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 20]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 32]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 2]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 10]>
nss_rs::p11::randomize::<&mut [u8]>
Line
Count
Source
353
18
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
354
18
    let m_buf = buf.as_mut();
355
18
    let len = c_int::try_from(m_buf.len()).expect("usize fits into c_int");
356
18
    secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
357
18
    buf
358
18
}
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 8]>
Unexecuted instantiation: nss_rs::p11::randomize::<&mut [u8]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 16]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 1]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 2]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 4]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 8]>
nss_rs::p11::randomize::<&mut smallvec::SmallVec<[u8; 20]>>
Line
Count
Source
353
1.28k
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
354
1.28k
    let m_buf = buf.as_mut();
355
1.28k
    let len = c_int::try_from(m_buf.len()).expect("usize fits into c_int");
356
1.28k
    secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
357
1.28k
    buf
358
1.28k
}
nss_rs::p11::randomize::<&mut [u8]>
Line
Count
Source
353
7
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
354
7
    let m_buf = buf.as_mut();
355
7
    let len = c_int::try_from(m_buf.len()).expect("usize fits into c_int");
356
7
    secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
357
7
    buf
358
7
}
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 16]>
nss_rs::p11::randomize::<[u8; 48]>
Line
Count
Source
353
598
pub fn randomize<B: AsMut<[u8]>>(mut buf: B) -> B {
354
598
    let m_buf = buf.as_mut();
355
598
    let len = c_int::try_from(m_buf.len()).expect("usize fits into c_int");
356
598
    secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).expect("NSS failed");
357
598
    buf
358
598
}
Unexecuted instantiation: nss_rs::p11::randomize::<&mut [u8]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 2]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 10]>
Unexecuted instantiation: nss_rs::p11::randomize::<&mut [u8]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 2]>
Unexecuted instantiation: nss_rs::p11::randomize::<[u8; 10]>
Unexecuted instantiation: nss_rs::p11::randomize::<&mut [u8]>
359
360
struct RandomCache {
361
    cache: [u8; Self::SIZE],
362
    used: usize,
363
}
364
365
impl RandomCache {
366
    const SIZE: usize = 4096;
367
    const CUTOFF: usize = 32;
368
369
    // Const constructor for compile-time initialization in thread_local!.
370
    // Cannot derive Default because `used` must be SIZE, not 0.
371
0
    const fn new() -> Self {
372
0
        Self {
373
0
            cache: [0; Self::SIZE],
374
0
            used: Self::SIZE,
375
0
        }
376
0
    }
377
378
13.3k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
13.3k
        let m_buf = buf.as_mut();
380
13.3k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
13.3k
        let avail = Self::SIZE - self.used;
382
13.3k
        if m_buf.len() <= avail {
383
13.3k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
13.3k
            self.used += m_buf.len();
385
13.3k
        } else {
386
25
            if avail > 0 {
387
22
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
22
            }
389
25
            randomize(&mut self.cache[..]);
390
25
            self.used = m_buf.len() - avail;
391
25
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
13.3k
        buf
394
13.3k
    }
<nss_rs::p11::RandomCache>::randomize::<[u8; 20]>
Line
Count
Source
378
2.57k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
2.57k
        let m_buf = buf.as_mut();
380
2.57k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
2.57k
        let avail = Self::SIZE - self.used;
382
2.57k
        if m_buf.len() <= avail {
383
2.56k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
2.56k
            self.used += m_buf.len();
385
2.56k
        } else {
386
18
            if avail > 0 {
387
17
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
17
            }
389
18
            randomize(&mut self.cache[..]);
390
18
            self.used = m_buf.len() - avail;
391
18
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
2.57k
        buf
394
2.57k
    }
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 32]>
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 2]>
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 10]>
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 8]>
<nss_rs::p11::RandomCache>::randomize::<[u8; 16]>
Line
Count
Source
378
1.77k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
1.77k
        let m_buf = buf.as_mut();
380
1.77k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
1.77k
        let avail = Self::SIZE - self.used;
382
1.77k
        if m_buf.len() <= avail {
383
1.76k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
1.76k
            self.used += m_buf.len();
385
1.76k
        } else {
386
4
            if avail > 0 {
387
4
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
4
            }
389
4
            randomize(&mut self.cache[..]);
390
4
            self.used = m_buf.len() - avail;
391
4
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
1.77k
        buf
394
1.77k
    }
<nss_rs::p11::RandomCache>::randomize::<[u8; 1]>
Line
Count
Source
378
4.03k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
4.03k
        let m_buf = buf.as_mut();
380
4.03k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
4.03k
        let avail = Self::SIZE - self.used;
382
4.03k
        if m_buf.len() <= avail {
383
4.02k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
4.02k
            self.used += m_buf.len();
385
4.02k
        } else {
386
2
            if avail > 0 {
387
0
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
2
            }
389
2
            randomize(&mut self.cache[..]);
390
2
            self.used = m_buf.len() - avail;
391
2
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
4.03k
        buf
394
4.03k
    }
<nss_rs::p11::RandomCache>::randomize::<[u8; 2]>
Line
Count
Source
378
1.12k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
1.12k
        let m_buf = buf.as_mut();
380
1.12k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
1.12k
        let avail = Self::SIZE - self.used;
382
1.12k
        if m_buf.len() <= avail {
383
1.12k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
1.12k
            self.used += m_buf.len();
385
1.12k
        } else {
386
0
            if avail > 0 {
387
0
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
0
            }
389
0
            randomize(&mut self.cache[..]);
390
0
            self.used = m_buf.len() - avail;
391
0
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
1.12k
        buf
394
1.12k
    }
<nss_rs::p11::RandomCache>::randomize::<[u8; 4]>
Line
Count
Source
378
2.57k
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
2.57k
        let m_buf = buf.as_mut();
380
2.57k
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
2.57k
        let avail = Self::SIZE - self.used;
382
2.57k
        if m_buf.len() <= avail {
383
2.57k
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
2.57k
            self.used += m_buf.len();
385
2.57k
        } else {
386
1
            if avail > 0 {
387
1
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
1
            }
389
1
            randomize(&mut self.cache[..]);
390
1
            self.used = m_buf.len() - avail;
391
1
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
2.57k
        buf
394
2.57k
    }
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 8]>
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 16]>
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 48]>
<nss_rs::p11::RandomCache>::randomize::<[u8; 2]>
Line
Count
Source
378
484
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
484
        let m_buf = buf.as_mut();
380
484
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
484
        let avail = Self::SIZE - self.used;
382
484
        if m_buf.len() <= avail {
383
484
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
484
            self.used += m_buf.len();
385
484
        } else {
386
0
            if avail > 0 {
387
0
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
0
            }
389
0
            randomize(&mut self.cache[..]);
390
0
            self.used = m_buf.len() - avail;
391
0
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
484
        buf
394
484
    }
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 10]>
<nss_rs::p11::RandomCache>::randomize::<[u8; 2]>
Line
Count
Source
378
805
    fn randomize<B: AsMut<[u8]>>(&mut self, mut buf: B) -> B {
379
805
        let m_buf = buf.as_mut();
380
805
        debug_assert!(m_buf.len() <= Self::CUTOFF);
381
805
        let avail = Self::SIZE - self.used;
382
805
        if m_buf.len() <= avail {
383
805
            m_buf.copy_from_slice(&self.cache[self.used..self.used + m_buf.len()]);
384
805
            self.used += m_buf.len();
385
805
        } else {
386
0
            if avail > 0 {
387
0
                m_buf[..avail].copy_from_slice(&self.cache[self.used..]);
388
0
            }
389
0
            randomize(&mut self.cache[..]);
390
0
            self.used = m_buf.len() - avail;
391
0
            m_buf[avail..].copy_from_slice(&self.cache[..self.used]);
392
        }
393
805
        buf
394
805
    }
Unexecuted instantiation: <nss_rs::p11::RandomCache>::randomize::<[u8; 10]>
395
}
396
397
/// Generate a randomized array.
398
///
399
/// # Panics
400
///
401
/// When `size` is too large or NSS fails.
402
#[must_use]
403
13.9k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
13.9k
    let buf = [0; N];
407
13.9k
    if N <= RandomCache::CUTOFF {
408
13.3k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
nss_rs::p11::random::<20>::{closure#0}
Line
Count
Source
408
2.57k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
Unexecuted instantiation: nss_rs::p11::random::<32>::{closure#0}
Unexecuted instantiation: nss_rs::p11::random::<2>::{closure#0}
Unexecuted instantiation: nss_rs::p11::random::<10>::{closure#0}
Unexecuted instantiation: nss_rs::p11::random::<8>::{closure#0}
nss_rs::p11::random::<16>::{closure#0}
Line
Count
Source
408
1.77k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
nss_rs::p11::random::<1>::{closure#0}
Line
Count
Source
408
4.03k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
nss_rs::p11::random::<2>::{closure#0}
Line
Count
Source
408
1.12k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
nss_rs::p11::random::<4>::{closure#0}
Line
Count
Source
408
2.57k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
Unexecuted instantiation: nss_rs::p11::random::<8>::{closure#0}
Unexecuted instantiation: nss_rs::p11::random::<16>::{closure#0}
Unexecuted instantiation: nss_rs::p11::random::<48>::{closure#0}
nss_rs::p11::random::<2>::{closure#0}
Line
Count
Source
408
484
        CACHE.with_borrow_mut(|c| c.randomize(buf))
Unexecuted instantiation: nss_rs::p11::random::<10>::{closure#0}
nss_rs::p11::random::<2>::{closure#0}
Line
Count
Source
408
805
        CACHE.with_borrow_mut(|c| c.randomize(buf))
Unexecuted instantiation: nss_rs::p11::random::<10>::{closure#0}
409
    } else {
410
598
        randomize(buf)
411
    }
412
13.9k
}
nss_rs::p11::random::<20>
Line
Count
Source
403
2.57k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
2.57k
    let buf = [0; N];
407
2.57k
    if N <= RandomCache::CUTOFF {
408
2.57k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
2.57k
}
Unexecuted instantiation: nss_rs::p11::random::<32>
Unexecuted instantiation: nss_rs::p11::random::<2>
Unexecuted instantiation: nss_rs::p11::random::<10>
Unexecuted instantiation: nss_rs::p11::random::<8>
nss_rs::p11::random::<16>
Line
Count
Source
403
1.77k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
1.77k
    let buf = [0; N];
407
1.77k
    if N <= RandomCache::CUTOFF {
408
1.77k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
1.77k
}
nss_rs::p11::random::<1>
Line
Count
Source
403
4.03k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
4.03k
    let buf = [0; N];
407
4.03k
    if N <= RandomCache::CUTOFF {
408
4.03k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
4.03k
}
nss_rs::p11::random::<2>
Line
Count
Source
403
1.12k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
1.12k
    let buf = [0; N];
407
1.12k
    if N <= RandomCache::CUTOFF {
408
1.12k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
1.12k
}
nss_rs::p11::random::<4>
Line
Count
Source
403
2.57k
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
2.57k
    let buf = [0; N];
407
2.57k
    if N <= RandomCache::CUTOFF {
408
2.57k
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
2.57k
}
Unexecuted instantiation: nss_rs::p11::random::<8>
Unexecuted instantiation: nss_rs::p11::random::<16>
nss_rs::p11::random::<48>
Line
Count
Source
403
598
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
598
    let buf = [0; N];
407
598
    if N <= RandomCache::CUTOFF {
408
0
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
598
        randomize(buf)
411
    }
412
598
}
nss_rs::p11::random::<2>
Line
Count
Source
403
484
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
484
    let buf = [0; N];
407
484
    if N <= RandomCache::CUTOFF {
408
484
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
484
}
Unexecuted instantiation: nss_rs::p11::random::<10>
nss_rs::p11::random::<2>
Line
Count
Source
403
805
pub fn random<const N: usize>() -> [u8; N] {
404
    thread_local!(static CACHE: RefCell<RandomCache> = const { RefCell::new(RandomCache::new()) });
405
406
805
    let buf = [0; N];
407
805
    if N <= RandomCache::CUTOFF {
408
805
        CACHE.with_borrow_mut(|c| c.randomize(buf))
409
    } else {
410
0
        randomize(buf)
411
    }
412
805
}
Unexecuted instantiation: nss_rs::p11::random::<10>
413
414
impl_into_result!(SECOidData);
415
416
#[cfg(test)]
417
#[cfg_attr(coverage_nightly, coverage(off))]
418
mod test {
419
    use std::ptr::null_mut;
420
421
    use test_fixture::fixture_init;
422
423
    use super::RandomCache;
424
    use crate::{PrivateKey, PublicKey, random};
425
426
    #[cfg(not(feature = "disable-random"))]
427
    #[test]
428
    fn randomness() {
429
        use crate::randomize;
430
431
        fixture_init();
432
        // If any of these ever fail, there is either a bug, or it's time to buy a lottery ticket.
433
        assert_ne!(random::<16>(), randomize([0; 16]));
434
        assert_ne!([0; 16], random::<16>());
435
        assert_ne!([0; 64], random::<64>());
436
    }
437
438
    #[test]
439
    fn cache_random_lengths() {
440
        const ZERO: [u8; 256] = [0; 256];
441
442
        fixture_init();
443
        let mut cache = RandomCache::new();
444
        let mut buf = [0; 256];
445
        let bits = usize::BITS - (RandomCache::CUTOFF - 1).leading_zeros();
446
        let mask = 0xff >> (u8::BITS - bits);
447
448
        for _ in 0..100 {
449
            let len = loop {
450
                let len = usize::from(random::<1>()[0] & mask) + 1;
451
                if len <= RandomCache::CUTOFF {
452
                    break len;
453
                }
454
            };
455
            buf.fill(0);
456
            if len >= 16 {
457
                assert_ne!(&cache.randomize(&mut buf[..len])[..len], &ZERO[..len]);
458
            }
459
        }
460
    }
461
462
    #[test]
463
    fn key_operations() {
464
        use crate::ech::generate_keys;
465
466
        fixture_init();
467
        let (sk, pk) = generate_keys().unwrap();
468
469
        // Test key_data serialization - X25519 keys are 32 bytes
470
        assert_eq!(pk.key_data().unwrap().len(), 32);
471
472
        // Test Debug formatting
473
        let pk_dbg = format!("{pk:?}");
474
        assert_eq!(&pk_dbg[..9], "PublicKey");
475
        let sk_dbg = format!("{sk:?}");
476
        // Private key debug output depends on whether key extraction is allowed by NSS.
477
        // It could be either "PrivateKey [hex]" or "Opaque PrivateKey".
478
        assert!(
479
            sk_dbg.starts_with("PrivateKey") || sk_dbg.starts_with("Opaque"),
480
            "unexpected private key debug format: {sk_dbg}"
481
        );
482
483
        // Test cloning
484
        let pk2 = pk.clone();
485
        let sk2 = sk.clone();
486
        assert_eq!(pk.key_data().unwrap(), pk2.key_data().unwrap());
487
        assert_eq!(format!("{sk:?}"), format!("{sk2:?}"));
488
    }
489
490
    #[test]
491
    fn null_pointer_error() {
492
        fixture_init();
493
        assert!(PublicKey::from_ptr(null_mut()).is_err());
494
        assert!(PrivateKey::from_ptr(null_mut()).is_err());
495
    }
496
}