// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use super::err::{secstatus_to_res, Error}; use crate::err::Res; use std::{ convert::TryFrom, marker::PhantomData, mem, os::raw::{c_int, c_uint}, ptr::null_mut, }; #[allow( clippy::pedantic, clippy::upper_case_acronyms, dead_code, deref_nullptr, non_camel_case_types, non_snake_case, non_upper_case_globals )] pub mod sys { include!(concat!(env!("OUT_DIR"), "/nss_p11.rs")); } use sys::{ PK11ObjectType, PK11SlotInfo, PK11SymKey, PK11_ExtractKeyValue, PK11_FreeSlot, PK11_FreeSymKey, PK11_GenerateRandom, PK11_GetInternalSlot, PK11_GetKeyData, PK11_ReadRawAttribute, PK11_ReferenceSymKey, PRBool, SECITEM_FreeItem, SECItem, SECItemType, SECKEYPrivateKey, SECKEYPublicKey, SECKEY_DestroyPrivateKey, SECKEY_DestroyPublicKey, CKA_VALUE, CK_ATTRIBUTE_TYPE, }; macro_rules! scoped_ptr { ($scoped:ident, $target:ty, $dtor:path) => { pub struct $scoped { ptr: *mut $target, } impl $scoped { pub fn from_ptr(ptr: *mut $target) -> Result { if ptr.is_null() { Err(crate::nss::err::Error::last()) } else { Ok(Self { ptr }) } } } impl std::ops::Deref for $scoped { type Target = *mut $target; #[must_use] fn deref(&self) -> &*mut $target { &self.ptr } } impl std::ops::DerefMut for $scoped { fn deref_mut(&mut self) -> &mut *mut $target { &mut self.ptr } } impl Drop for $scoped { fn drop(&mut self) { let _ = unsafe { $dtor(self.ptr) }; } } }; } scoped_ptr!(PrivateKey, SECKEYPrivateKey, SECKEY_DestroyPrivateKey); impl PrivateKey { pub fn key_data(&self) -> Res> { let mut key_item = SECItem { type_: SECItemType::siBuffer, data: null_mut(), len: 0, }; secstatus_to_res(unsafe { PK11_ReadRawAttribute( PK11ObjectType::PK11_TypePrivKey, (**self).cast(), CK_ATTRIBUTE_TYPE::from(CKA_VALUE), &mut key_item, ) })?; let slc = unsafe { std::slice::from_raw_parts(key_item.data, usize::try_from(key_item.len).unwrap()) }; let key = Vec::from(slc); // The data that `key_item` refers to needs to be freed, but we can't // use the scoped `Item` implementation. This is OK as long as nothing // panics between `PK11_ReadRawAttribute` succeeding and here. unsafe { SECITEM_FreeItem(&mut key_item, PRBool::from(false)); } Ok(key) } } unsafe impl Send for PrivateKey {} impl Clone for PrivateKey { #[must_use] fn clone(&self) -> Self { let ptr = unsafe { sys::SECKEY_CopyPrivateKey(self.ptr) }; assert!(!ptr.is_null()); Self { ptr } } } impl std::fmt::Debug for PrivateKey { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Ok(b) = self.key_data() { write!(f, "PrivateKey {}", hex::encode(b)) } else { write!(f, "Opaque PrivateKey") } } } scoped_ptr!(PublicKey, SECKEYPublicKey, SECKEY_DestroyPublicKey); impl PublicKey { /// Get the HPKE serialization of the public key. pub fn key_data(&self) -> Res> { let mut buf = vec![0; 100]; let mut len: c_uint = 0; secstatus_to_res(unsafe { sys::PK11_HPKE_Serialize( **self, buf.as_mut_ptr(), &mut len, c_uint::try_from(buf.len()).unwrap(), ) })?; buf.truncate(usize::try_from(len).unwrap()); Ok(buf) } } unsafe impl Send for PublicKey {} impl Clone for PublicKey { #[must_use] fn clone(&self) -> Self { let ptr = unsafe { sys::SECKEY_CopyPublicKey(self.ptr) }; assert!(!ptr.is_null()); Self { ptr } } } impl std::fmt::Debug for PublicKey { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Ok(b) = self.key_data() { write!(f, "PublicKey {}", hex::encode(b)) } else { write!(f, "Opaque PublicKey") } } } scoped_ptr!(Slot, PK11SlotInfo, PK11_FreeSlot); impl Slot { pub(crate) fn internal() -> Res { let p = unsafe { PK11_GetInternalSlot() }; Slot::from_ptr(p) } } scoped_ptr!(SymKey, PK11SymKey, PK11_FreeSymKey); impl SymKey { /// You really don't want to use this. /// /// # Errors /// Some keys cannot be inspected in this way. /// Also, internal errors in case of failures in NSS. pub fn key_data(&self) -> Res<&[u8]> { secstatus_to_res(unsafe { PK11_ExtractKeyValue(self.ptr) })?; let key_item = unsafe { PK11_GetKeyData(self.ptr) }; // This is accessing a value attached to the key, so we can treat this as a borrow. match unsafe { key_item.as_mut() } { None => Err(Error::last()), Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }), } } } impl Clone for SymKey { #[must_use] fn clone(&self) -> Self { let ptr = unsafe { PK11_ReferenceSymKey(self.ptr) }; assert!(!ptr.is_null()); Self { ptr } } } impl std::fmt::Debug for SymKey { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Ok(b) = self.key_data() { write!(f, "SymKey {}", hex::encode(b)) } else { write!(f, "Opaque SymKey") } } } unsafe impl Send for SymKey {} /// Generate a randomized buffer. #[must_use] pub fn random(size: usize) -> Vec { let mut buf = vec![0; size]; secstatus_to_res(unsafe { PK11_GenerateRandom(buf.as_mut_ptr(), c_int::try_from(buf.len()).unwrap()) }) .unwrap(); buf } pub(crate) struct ParamItem<'a, T: 'a> { item: SECItem, marker: PhantomData<&'a T>, } impl<'a, T: Sized + 'a> ParamItem<'a, T> { pub fn new(v: &'a mut T) -> Self { let item = SECItem { type_: SECItemType::siBuffer, data: (v as *mut T).cast::(), len: c_uint::try_from(mem::size_of::()).unwrap(), }; Self { item, marker: PhantomData::default(), } } pub fn ptr(&mut self) -> *mut SECItem { std::ptr::addr_of_mut!(self.item) } } unsafe fn destroy_secitem(item: *mut SECItem) { SECITEM_FreeItem(item, PRBool::from(true)); } scoped_ptr!(Item, SECItem, destroy_secitem); impl Item { /// Create a wrapper for a slice of this object. /// Creating this object is technically safe, but using it is extremely dangerous. /// Minimally, it can only be passed as a `const SECItem*` argument to functions. pub(crate) fn wrap(buf: &[u8]) -> SECItem { SECItem { type_: SECItemType::siBuffer, data: buf.as_ptr() as *mut u8, len: c_uint::try_from(buf.len()).unwrap(), } } /// This dereferences the pointer held by the item and makes a copy of the /// content that is referenced there. /// /// # Safety /// This dereferences two pointers. It doesn't get much less safe. pub(crate) unsafe fn into_vec(self) -> Vec { let b = self.ptr.as_ref().unwrap(); // Sanity check the type, as some types don't count bytes in `Item::len`. assert_eq!(b.type_, SECItemType::siBuffer); let slc = std::slice::from_raw_parts(b.data, usize::try_from(b.len).unwrap()); Vec::from(slc) } } #[cfg(test)] mod test { use super::random; use crate::init; #[test] fn randomness() { init(); // If this ever fails, there is either a bug, or it's time to buy a lottery ticket. assert_ne!(random(16), random(16)); } }