Revision control

Copy as Markdown

Other Tools

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Self, crate::err::Error> {
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) {
unsafe { $dtor(self.ptr) };
}
}
};
}
scoped_ptr!(PrivateKey, SECKEYPrivateKey, SECKEY_DestroyPrivateKey);
impl PrivateKey {
pub fn key_data(&self) -> Res<Vec<u8>> {
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<Vec<u8>> {
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<Self> {
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<u8> {
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::<u8>(),
len: c_uint::try_from(mem::size_of::<T>()).unwrap(),
};
Self {
item,
marker: PhantomData,
}
}
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<u8> {
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));
}
}