rdseed 0.1.0-beta.10

Rust interface for RDRAND / RDSEED CPU instructions
Documentation
// Rust interface to RDSEED / RDRAND
//
// Written by Radim Kolar <hsn@sendmail.cz> 2026
//
// This is free and unencumbered software released into the public domain.
// SPDX-License-Identifier: CC0-1.0 OR Unlicense

//! # rdseed
//! Rust interface to RDSEED / RDRAND CPU instructions.
//!
//! # About
//! Get 64, 32, 16 bit RDSEED or RDRAND from the hardware,
//! fill a byte slice.
//!
//! RDSEED numbers are trully random, while RDRAND numbers are
//! from cryptographic strong pseudo random number generator.
//!
//! # Usage
//! Functions are nonblocking returning an `Option` type.
//!
//! Operation can fail if hardware do not supports instruction
//! or if no randomness is available at this moment.
//!
//! Functions from module [rand] are using strong pseudo random
//! number generator and will less likely to fail due
//! to no randomness available at this moment.
//!
//! To query if hardware supports operation use
//! [is_available] function.
//!
//! To block until randomness is available use
//! [blocking] wrapper function.
//!
//! For filling a byte slice use blocking function [fill_bytes].
//!
//! # Portability
//! x64 CPU with RDSEED / RDRAND instructions.
//!
//! # License
//! This is free and unencumbered software released into the public domain.
//!
//! This code can be used under terms of [CC0](https://creativecommons.org/publicdomain/zero/1.0/) or
//! the [Unlicense](https://unlicense.org).
//!
//! ![Unlicense logo](https://unlicense.org/pd-icon.png)

/// Attempts to get a 64-bit seed from the hardware.
///
/// ## Returns
///   Some(u64) if successful.
///   None if the hardware was busy or operation is not supported.
///
/// ## See also
///
/// 1. [is_available] - checks if hardware supports RDSEED instruction.
/// 1. [get32] - get 32-bit seed from the hardware.
/// 1. [get16] - get 16-bit seed from the hardware.
pub fn get64() -> Option<u64> {
   #[cfg(target_arch = "x86_64")]
   {
      use std::arch::x86_64::_rdseed64_step;

      // Check if the CPU supports RDSEED at runtime is recommended,
      // but for the raw instruction call:
      let mut seed = 0u64;
      unsafe {
         // _rdseed64_step returns 1 if a valid value was obtained
         if _rdseed64_step(&mut seed) == 1 {
            return Some(seed);
         }
      }
   }
   None
}

/// Attempts to get a 32-bit seed from the hardware.
///
/// ## Returns
///   Some(u32) if successful.
///   None if the hardware was busy or operation is not supported.
///
/// ## See also
///
/// 1. [is_available] - checks if hardware supports RDSEED instruction.
/// 1. [get64] - get 64-bit seed from the hardware.
/// 1. [get16] - get 16-bit seed from the hardware.
pub fn get32() -> Option<u32> {
   #[cfg(target_arch = "x86_64")]
   {
      use std::arch::x86_64::_rdseed32_step;

      // Check if the CPU supports RDSEED at runtime is recommended,
      // but for the raw instruction call:
      let mut seed = 0u32;
      unsafe {
         // _rdseed32_step returns 1 if a valid value was obtained
         if _rdseed32_step(&mut seed) == 1 {
            return Some(seed);
         }
      }
   }
   None
}

/// Attempts to get a 16-bit seed from the hardware.
///
/// ## Returns
///   Some(u16) if successful.
///   None if the hardware was busy or operation is not supported.
///
/// ## See also
///
/// 1. [is_available] - checks if hardware supports RDSEED instruction.
/// 1. [get64] - get 64-bit seed from the hardware.
/// 1. [get32] - get 32-bit seed from the hardware.
pub fn get16() -> Option<u16> {
   #[cfg(target_arch = "x86_64")]
   {
      use std::arch::x86_64::_rdseed16_step;

      // Check if the CPU supports RDSEED at runtime is recommended,
      // but for the raw instruction call:
      let mut seed = 0u16;
      unsafe {
         // _rdseed16_step returns 1 if a valid value was obtained
         if _rdseed16_step(&mut seed) == 1 {
            return Some(seed);
         }
      }
   }
   None
}

/// Checks if hardware supports RDSEED instruction.
///
/// ## Returns
///   true if the current CPU supports the RDSEED instruction.
pub fn is_available() -> bool {
   #[cfg(target_arch = "x86_64")]
   {
      // This macro performs a runtime check using the CPUID instruction
      if is_x86_feature_detected!("rdseed") {
         return true;
      }
   }

   // Always return false if not on x86_64 or if feature is missing
   false
}

/// Block until randomness is available.
///
/// Wrapper which spins until randomness is available.
///
/// ## Parameters
///   f - function returning Option<T>. You can choose
///       any type and size you want.
///       Pass function *getXX* from main or rand module.
/// ## Returns
///   returns unsigned number, not `Option`.
///
/// ## Errors
///   function panics if operation is not supported by hardware.
/// ## Example
/// ```
/// let seed: u32 = rdseed::blocking(rdseed::get32);
/// ```
pub fn blocking<T>(f: impl Fn() -> Option<T>) -> T {
   let mut rc = f();
   if rc.is_some() {
      return rc.unwrap();
   }
   if !is_available() {
      panic!("Instruction not supported by hardware");
   }
   // spin until randomness is available
   loop {
      rc = f();
      if rc.is_some() {
         return rc.unwrap();
      }
   }
}

/// Fills u8 buffer with random bytes generated by the provided function.
///
/// This function splits the buffer into chunks of up to 8 bytes and fills each chunk
/// with bytes derived from a `u64` value generated by `rng` function argument.
///
/// ## Parameters
///
/// - `buffer`: A mutable reference to a slice of bytes (`&mut [u8]`) that will be filled with random data.
/// - `rng`: A function (`Fn() -> Option<u64>`) that generates random `u64` values.
///
/// ## Example
///
/// ```
/// let mut data = [0u8; 16];
/// rdseed::fill_bytes(&mut data, rdseed::get64);
///
/// println!("{:?}", data); // Randomized output
/// ```
///
/// ## Safety Notes
///
/// - The randomness depends on the quality of the provided `rng` function.
/// - This function uses [blocking] function for extracting random data from `rng` function.
///   It will panic if hardware do not supports random generator operations.
pub fn fill_bytes(buffer: &mut [u8], rng: impl Fn() -> Option<u64>) {
   if buffer.is_empty() {
      return; // No need to process an empty slice
   }

   for chunk in buffer.chunks_mut(8) {
      let random_bytes = blocking(&rng).to_be_bytes();
      chunk.copy_from_slice(&random_bytes[..chunk.len()]);
   }
}

/// Interface to RDRAND, strong pseudo random number generator.
#[path = "lib_rand.rs"]
pub mod rand;