短小精悍(4) - Rust操作系统随机数getrandom库介绍

发布时间 2023-12-28 09:46:35作者: Cinea

今天带来的是另一个“短小精悍”的库:getrandom。它的作用是从操作系统提供的随机数源获得一段随机数。

用法

getrandom的用法很简单,唯一需要了解的就是它内部的同名函数:

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error>

它将会向dest中填充来自操作系统的随机数。

示例:

fn main() {
    let mut data = [0u8; 8];
    getrandom::getrandom(&mut data).unwrap();
    println!("{:?}", data);
}

// $ ./playground
// [186, 180, 148, 20, 13, 30, 186, 46]

用途

首先,读者应当了解一点:getrandom在性能上并不如rand库的thread_rng,它并不适合被放置在热点路径上反复调用。getrandom应当被用来生成那种“一次性使用”的随机数,例如在其他随机数算法中使用的种子。

例如,在默认的feature flags下,著名的加密库ahash便使用getrandom库来为Hasher在运行时提供种子:

// aHash/src/random_state.rs:73
if #[cfg(all(feature = "runtime-rng", not(fuzzing)))] {
    #[inline]
    fn get_fixed_seeds() -> &'static [[u64; 4]; 2] {
        use crate::convert::Convert;

        static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new();

        SEEDS.get_or_init(|| {
            let mut result: [u8; 64] = [0; 64];
            getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
            Box::new(result.convert())
        })
    }
}

uuidgetrandom库的态度也能佐证这一点:

// uuid/src/rng.rs:1
#[cfg(any(feature = "v4", feature = "v7"))]
pub(crate) fn bytes() -> [u8; 16] {
    #[cfg(not(feature = "fast-rng"))]
    {
        let mut bytes = [0u8; 16];

        getrandom::getrandom(&mut bytes).unwrap_or_else(|err| {
            // NB: getrandom::Error has no source; this is adequate display
            panic!("could not retrieve random bytes for uuid: {}", err)
        });

        bytes
    }

    #[cfg(feature = "fast-rng")]
    {
        rand::random()
    }
}

uuid默认开启的特性fast-rng下,uuid将会使用rand库来生成随机数,因为这比直接使用getrandom快得多。

所以,对于大多数情况来说,仅仅使用randthread_rng就已经完全足够了;当读者计划使用getrandom时,建议读者最好能够确定自己真的需要它。

原理和细节

getrandom库使用了来自操作系统的接口来生成随机数,具体来说,在Linux上使用getrandomman7.org),在Windows上使用BCryptGenRandomWindows Learn),在macOS上使用getentropyunix.com)。

getrandom库在遇到错误时会优先返回Error,而不是不可信的结果。

getrandom库在系统启动的早期可能会因为操作系统还没有安全地为RNG设置种子而阻塞。