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