1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Type-safe bindings for the Zircon kernel's CPRNG.
//!
use crate::{ok, Status};
use fuchsia_zircon_sys as sys;
use std::mem::MaybeUninit;

/// Draw random bytes from the kernel's CPRNG to fill `buffer`. This function
/// always fills the buffer.
///
/// Wraps the
/// [zx_cprng_draw](https://fuchsia.dev/fuchsia-src/reference/syscalls/cprng_draw.md)
/// syscall.
pub fn cprng_draw(buffer: &mut [u8]) {
    // SAFETY: &[u8] and &[MaybeUninit<u8>] have the same layout.
    let buffer = unsafe {
        std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut MaybeUninit<u8>, buffer.len())
    };

    cprng_draw_uninit(buffer);
}

/// Draw random bytes from the kernel's CPRNG to fill `buffer`. This function
/// always fills the buffer.
///
/// Wraps the
/// [zx_cprng_draw](https://fuchsia.dev/fuchsia-src/reference/syscalls/cprng_draw.md)
/// syscall.
///
/// Returns the filled buffer.
pub fn cprng_draw_uninit(buffer: &mut [MaybeUninit<u8>]) -> &mut [u8] {
    // SAFETY: `buffer` is a well formed slice of (maybe) uninitialized `u8`s.
    // `MaybeUninit<T>` and `T` have the same layout.
    unsafe { sys::zx_cprng_draw(buffer.as_mut_ptr() as *mut u8, buffer.len()) };

    // SAFETY: We're converting &mut [MaybeUninit<u8>] back to &mut [u8], which is only
    // valid to do if all elements of `buffer` have actually been initialized. Here we
    // have to trust that the kernel didn't lie when it said it wrote to the entire
    // buffer, but as long as that assumption is valid them it's safe to assume this
    // slice is init.
    //
    // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_assume_init_mut when stable
    unsafe { std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len()) }
}

/// Mix the given entropy into the kernel CPRNG.
///
/// The buffer must have length less than `ZX_CPRNG_ADD_ENTROPY_MAX_LEN`.
///
/// Wraps the
/// [zx_cprng_add_entropy](https://fuchsia.dev/fuchsia-src/reference/syscalls/cprng_add_entropy.md)
/// syscall.
pub fn cprng_add_entropy(buffer: &[u8]) -> Result<(), Status> {
    let status = unsafe { sys::zx_cprng_add_entropy(buffer.as_ptr(), buffer.len()) };
    ok(status)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn check_buffer(buffer: &mut [u8]) {
        let mut first_zero = 0;
        let mut last_zero = 0;
        for _ in 0..30 {
            cprng_draw(buffer);
            if buffer[0] == 0 {
                first_zero += 1;
            }
            if buffer.len() > 1 && buffer[buffer.len() - 1] == 0 {
                last_zero += 1;
            }
        }
        assert_ne!(first_zero, 30);
        assert_ne!(last_zero, 30);
    }

    #[test]
    fn cprng() {
        let mut buffer = [0; 20];
        check_buffer(&mut buffer);
    }

    #[test]
    fn cprng_large() {
        const SIZE: usize = sys::ZX_CPRNG_DRAW_MAX_LEN + 1;
        let mut buffer = [0; SIZE];
        check_buffer(&mut buffer);

        for mut s in buffer.chunks_mut(SIZE / 3) {
            check_buffer(&mut s);
        }
    }

    #[test]
    fn cprng_add() {
        let buffer = [0, 1, 2];
        assert_eq!(cprng_add_entropy(&buffer), Ok(()));
    }
}