use crate::error::ErrorStack;
use native_ossl_sys as sys;
use std::ffi::CStr;
use std::sync::Arc;
pub struct RandAlg {
ptr: *mut sys::EVP_RAND,
_lib_ctx: Option<Arc<crate::lib_ctx::LibCtx>>,
}
impl RandAlg {
pub fn fetch(name: &CStr, props: Option<&CStr>) -> Result<Self, ErrorStack> {
let props_ptr = props.map_or(std::ptr::null(), CStr::as_ptr);
let ptr = unsafe { sys::EVP_RAND_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(RandAlg {
ptr,
_lib_ctx: None,
})
}
pub fn fetch_in(
ctx: &Arc<crate::lib_ctx::LibCtx>,
name: &CStr,
props: Option<&CStr>,
) -> Result<Self, ErrorStack> {
let props_ptr = props.map_or(std::ptr::null(), CStr::as_ptr);
let ptr = unsafe { sys::EVP_RAND_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(RandAlg {
ptr,
_lib_ctx: Some(Arc::clone(ctx)),
})
}
#[must_use]
pub(crate) fn as_ptr(&self) -> *mut sys::EVP_RAND {
self.ptr
}
}
impl Drop for RandAlg {
fn drop(&mut self) {
unsafe { sys::EVP_RAND_free(self.ptr) };
}
}
unsafe impl Send for RandAlg {}
unsafe impl Sync for RandAlg {}
pub struct Rand;
impl Rand {
pub fn fill(buf: &mut [u8]) -> Result<(), crate::error::ErrorStack> {
let n = i32::try_from(buf.len()).expect("buffer too large for RAND_bytes");
let rc = unsafe { sys::RAND_bytes(buf.as_mut_ptr(), n) };
if rc != 1 {
return Err(crate::error::ErrorStack::drain());
}
Ok(())
}
pub fn fill_private(buf: &mut [u8]) -> Result<(), crate::error::ErrorStack> {
let n = i32::try_from(buf.len()).expect("buffer too large for RAND_priv_bytes");
let rc = unsafe { sys::RAND_priv_bytes(buf.as_mut_ptr(), n) };
if rc != 1 {
return Err(crate::error::ErrorStack::drain());
}
Ok(())
}
pub fn bytes(n: usize) -> Result<Vec<u8>, crate::error::ErrorStack> {
let mut buf = vec![0u8; n];
Self::fill(&mut buf)?;
Ok(buf)
}
pub fn bytes_private(n: usize) -> Result<Vec<u8>, crate::error::ErrorStack> {
let mut buf = vec![0u8; n];
Self::fill_private(&mut buf)?;
Ok(buf)
}
}
pub struct GenerateRequest<'a> {
pub strength: u32,
pub prediction_resistance: bool,
pub additional_input: Option<&'a [u8]>,
}
impl Default for GenerateRequest<'_> {
fn default() -> Self {
GenerateRequest {
strength: 256,
prediction_resistance: false,
additional_input: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandState {
Uninitialised,
Ready,
Error,
Unknown(i32),
}
pub struct RandCtx {
ptr: *mut sys::EVP_RAND_CTX,
}
impl RandCtx {
pub fn new(alg: &RandAlg, parent: Option<&RandCtx>) -> Result<Self, crate::error::ErrorStack> {
let parent_ptr = parent.map_or(std::ptr::null_mut(), |p| p.ptr);
let ptr = unsafe { sys::EVP_RAND_CTX_new(alg.as_ptr(), parent_ptr) };
if ptr.is_null() {
return Err(crate::error::ErrorStack::drain());
}
Ok(RandCtx { ptr })
}
pub fn instantiate(
&mut self,
strength: u32,
prediction_resistance: bool,
params: Option<&crate::params::Params<'_>>,
) -> Result<(), crate::error::ErrorStack> {
let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
let rc = unsafe {
sys::EVP_RAND_instantiate(
self.ptr,
strength,
i32::from(prediction_resistance),
std::ptr::null(),
0,
params_ptr,
)
};
if rc != 1 {
return Err(crate::error::ErrorStack::drain());
}
Ok(())
}
pub fn generate(
&mut self,
out: &mut [u8],
req: &GenerateRequest<'_>,
) -> Result<(), crate::error::ErrorStack> {
let (ai_ptr, ai_len) = req
.additional_input
.map_or((std::ptr::null(), 0), |s| (s.as_ptr(), s.len()));
let rc = unsafe {
sys::EVP_RAND_generate(
self.ptr,
out.as_mut_ptr(),
out.len(),
req.strength,
i32::from(req.prediction_resistance),
ai_ptr,
ai_len,
)
};
if rc != 1 {
return Err(crate::error::ErrorStack::drain());
}
Ok(())
}
pub fn fill(&mut self, out: &mut [u8]) -> Result<(), crate::error::ErrorStack> {
self.generate(out, &GenerateRequest::default())
}
#[must_use]
pub fn strength(&self) -> u32 {
unsafe { sys::EVP_RAND_get_strength(self.ptr) }
}
#[must_use]
pub fn state(&self) -> RandState {
match unsafe { sys::EVP_RAND_get_state(self.ptr) } {
0 => RandState::Uninitialised,
1 => RandState::Ready,
2 => RandState::Error,
n => RandState::Unknown(n),
}
}
#[cfg(ossl320)]
pub fn public() -> Result<GlobalRandCtx, crate::error::ErrorStack> {
let ptr = unsafe { sys::RAND_get0_public(std::ptr::null_mut()) };
if ptr.is_null() {
return Err(crate::error::ErrorStack::drain());
}
Ok(GlobalRandCtx(std::mem::ManuallyDrop::new(RandCtx { ptr })))
}
#[cfg(ossl320)]
pub fn private_global() -> Result<GlobalRandCtx, crate::error::ErrorStack> {
let ptr = unsafe { sys::RAND_get0_private(std::ptr::null_mut()) };
if ptr.is_null() {
return Err(crate::error::ErrorStack::drain());
}
Ok(GlobalRandCtx(std::mem::ManuallyDrop::new(RandCtx { ptr })))
}
}
#[cfg(ossl320)]
pub struct GlobalRandCtx(std::mem::ManuallyDrop<RandCtx>);
#[cfg(ossl320)]
unsafe impl Send for GlobalRandCtx {}
#[cfg(ossl320)]
impl std::ops::Deref for GlobalRandCtx {
type Target = RandCtx;
fn deref(&self) -> &RandCtx {
&self.0
}
}
#[cfg(ossl320)]
impl GlobalRandCtx {
#[must_use]
pub fn as_rand_ctx(&self) -> &RandCtx {
&self.0
}
}
#[cfg(ossl310)]
impl Clone for RandCtx {
fn clone(&self) -> Self {
unsafe { sys::EVP_RAND_CTX_up_ref(self.ptr) };
RandCtx { ptr: self.ptr }
}
}
impl Drop for RandCtx {
fn drop(&mut self) {
unsafe { sys::EVP_RAND_CTX_free(self.ptr) };
}
}
unsafe impl Send for RandCtx {}
unsafe impl Sync for RandCtx {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fetch_ctr_drbg_succeeds() {
let alg = RandAlg::fetch(c"CTR-DRBG", None).unwrap();
drop(alg);
}
#[test]
fn fetch_nonexistent_fails() {
assert!(RandAlg::fetch(c"NONEXISTENT_RAND_XYZ", None).is_err());
}
#[test]
fn rand_fill_nonzero() {
let mut buf = [0u8; 32];
Rand::fill(&mut buf).unwrap();
assert_ne!(buf, [0u8; 32]);
}
#[test]
fn rand_bytes_len() {
let v = Rand::bytes(64).unwrap();
assert_eq!(v.len(), 64);
}
#[test]
fn rand_ctx_two_outputs_differ() {
let alg = RandAlg::fetch(c"CTR-DRBG", None).unwrap();
let mut ctx = RandCtx::new(&alg, None).unwrap();
let params = crate::params::ParamBuilder::new()
.unwrap()
.push_utf8_string(c"cipher", c"AES-128-CTR")
.unwrap()
.build()
.unwrap();
ctx.instantiate(128, false, Some(¶ms)).unwrap();
let req = GenerateRequest {
strength: 128,
..Default::default()
};
let mut a = [0u8; 32];
let mut b = [0u8; 32];
ctx.generate(&mut a, &req).unwrap();
ctx.generate(&mut b, &req).unwrap();
assert_ne!(a, b);
}
}