Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
rust: Expose our (P)RNGs in Rust and provide safe wrappers.
* FIXES #24660: https://bugs.torproject.org/24660
- Loading branch information
Showing
with
538 additions
and 2 deletions.
- +19 −0 src/rust/Cargo.lock
- +3 −1 src/rust/Cargo.toml
- +3 −0 src/rust/external/Cargo.toml
- +177 −0 src/rust/external/crypto_rand.rs
- +5 −1 src/rust/external/lib.rs
- +30 −0 src/rust/rand/Cargo.toml
- +15 −0 src/rust/rand/lib.rs
- +184 −0 src/rust/rand/prng.rs
- +95 −0 src/rust/rand/rng.rs
- +7 −0 src/rust/rand/src/lib.rs
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -1,6 +1,8 @@ | ||
| [workspace] | ||
| members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", | ||
| "tor_rust", "tor_log"] | ||
| "tor_rust", "tor_log", | ||
| "rand", | ||
| ] | ||
|
|
||
| [profile.release] | ||
| debug = true | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -6,6 +6,9 @@ name = "external" | ||
| [dependencies] | ||
| libc = "=0.2.39" | ||
|
|
||
| [dependencies.smartlist] | ||
| path = "../smartlist" | ||
|
|
||
| [lib] | ||
| name = "external" | ||
| path = "lib.rs" | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,177 @@ | ||
| // Copyright (c) 2018, The Tor Project, Inc. | ||
| // Copyright (c) 2018, isis agora lovecruft | ||
| // See LICENSE for licensing information | ||
|
|
||
| //! Bindings to external (P)RNG interfaces and utilities in | ||
| //! src/common/crypto_rand.[ch]. | ||
| //! | ||
| //! We wrap our C implementations in src/common/crypto_rand.[ch] here in order | ||
| //! to provide wrappers with native Rust types, and then provide more Rusty | ||
| //! types and and trait implementations in src/rust/crypto/rand/. | ||
|
|
||
| use std::time::Duration; | ||
|
|
||
| use libc::c_char; | ||
| use libc::c_double; | ||
| use libc::c_int; | ||
| use libc::c_uint; | ||
| use libc::c_void; | ||
| use libc::size_t; | ||
| use libc::time_t; | ||
| use libc::uint8_t; | ||
| use libc::uint64_t; | ||
|
|
||
| use smartlist::Stringlist; | ||
|
|
||
| /// A `smartlist_t` is just an alias for the `#[repr(C)]` type `Stringlist`, to | ||
| /// make it more clear that we're working with a smartlist which is owned by C. | ||
| #[allow(non_camel_case_types)] | ||
| type smartlist_t = Stringlist; | ||
This comment has been minimized.
This comment has been minimized.
isislovecruft
Author
Owner
|
||
|
|
||
| extern "C" { | ||
| fn crypto_seed_rng() -> c_int; | ||
| fn crypto_rand(to: *mut c_char, n: size_t); | ||
| fn crypto_strongest_rand(out: *mut uint8_t, out_len: size_t); | ||
| fn crypto_rand_int(max: c_uint) -> c_int; | ||
| fn crypto_rand_int_range(min: c_uint, max: c_uint) -> c_int; | ||
| fn crypto_rand_uint64_range(min: uint64_t, max: uint64_t) -> uint64_t; | ||
| fn crypto_rand_time_range(min: time_t, max: time_t) -> time_t; | ||
| fn crypto_rand_uint64(max: uint64_t) -> uint64_t; | ||
| fn crypto_rand_double() -> c_double; | ||
| // fn crypto_init_siphash_key() -> c_int; | ||
| // fn crypto_random_hostname(min_rand_len: c_int, max_rand_len: c_int, | ||
| // prefix: *const c_char, suffix: *const c_char) -> *mut c_char; | ||
| /* XXX c_void is an enum, is it actually safe to pass over the boundary? | ||
| * cf. https://doc.rust-lang.org/libc/x86_64-unknown-linux-gnu/src/libc/lib.rs.html#111-121 | ||
| * --isis | ||
| */ | ||
| // fn smartlist_choose(sl: *const smartlist_t) -> *mut c_void; | ||
| // fn smartlist_shuffle(sl: *mut smartlist_t); | ||
| // fn crypto_force_rand_ssleay() -> c_int; | ||
| } | ||
|
|
||
| /// Seed OpenSSL's random number generator with bytes from the operating | ||
| /// system. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// `true` on success; `false` on failure. | ||
| pub fn c_tor_crypto_seed_rng() -> bool { | ||
This comment has been minimized.
nmathewson
|
||
| let ret: c_int; | ||
|
|
||
| unsafe { | ||
| ret = crypto_seed_rng(); | ||
| } | ||
| match ret { | ||
| 0 => return true, | ||
| _ => return false, | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /// Fill the bytes of `dest` with strong random data. | ||
| /// | ||
| /// Supports mocking for unit tests. | ||
| /// | ||
| /// # Abortions | ||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized. |
||
| /// | ||
| /// This function is not allowed to fail; if it would fail to generate strong | ||
| /// entropy, it must terminate the process instead. | ||
| pub fn c_tor_crypto_rand(dest: &mut [u8]) { | ||
| // We'll let the C side panic if the len is larger than | ||
| // MAX_STRONGEST_RAND_SIZE, rather than potentially panicking here. A | ||
| // paranoid caller should assert on the length of dest *before* calling this | ||
| // function. | ||
| unsafe { | ||
| crypto_rand(dest.as_mut_ptr() as *mut c_char, dest.len() as size_t); | ||
| } | ||
| } | ||
|
|
||
| pub fn c_tor_crypto_strongest_rand(dest: &mut [u8]) { | ||
| unsafe { | ||
| crypto_strongest_rand(dest.as_mut_ptr(), dest.len() as size_t); | ||
| } | ||
| } | ||
|
|
||
| /// Return a pseudorandom integer, chosen uniformly from the values | ||
| /// between 0 and `max - 1`, inclusive. | ||
| /// | ||
| /// # Inputs | ||
| /// | ||
| /// `max` must be between 1 and INT_MAX+1, inclusive. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// (On the C side) if `max` isn't between 1 and INT_MAX+1. | ||
| pub fn c_tor_crypto_rand_int(max: &usize) -> i32 { | ||
This comment has been minimized.
nmathewson
|
||
| unsafe { | ||
| crypto_rand_int(*max as c_uint) | ||
| } | ||
| } | ||
|
|
||
| /// Return a pseudorandom integer, chosen uniformly from the values `i` such | ||
| /// that `min <= i < max`. | ||
| /// | ||
| /// # Inputs | ||
| /// | ||
| /// `min` MUST be in range `[0, max)`. | ||
| /// `max` MUST be in range `(min, INT_MAX]`. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// (On the C side) if the above two conditions aren't upheld. | ||
| pub fn c_tor_crypto_rand_int_range(min: &usize, max: &usize) -> i32 { | ||
| unsafe { | ||
| crypto_rand_int_range(*min as c_uint, *max as c_uint) | ||
| } | ||
| } | ||
|
|
||
| /// Return a pseudorandom 64-bit integer, chosen uniformly from the values | ||
| /// between 0 and `max - 1`, inclusive. | ||
| pub fn c_tor_crypto_rand_uint64(max: &u64) -> u64 { | ||
| unsafe { | ||
| crypto_rand_uint64(*max as uint64_t) | ||
| } | ||
| } | ||
|
|
||
| /// As crypto_rand_int_range, but supports 64-bit unsigned integers. | ||
| pub fn c_tor_crypto_rand_uint64_range(min: &u64, max: &u64) -> u64 { | ||
| unsafe { | ||
| crypto_rand_uint64_range(*min as uint64_t, *max as uint64_t) | ||
| } | ||
| } | ||
|
|
||
| /// Get a random time, in seconds since the Unix Epoch. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// A `std::time::Duration` of seconds since the Unix Epoch. | ||
| pub fn c_tor_crypto_rand_time_range(min: &Duration, max: &Duration) -> Duration { | ||
| let ret: time_t; | ||
|
|
||
| unsafe { | ||
| ret = crypto_rand_time_range(min.as_secs() as time_t, max.as_secs() as time_t); | ||
| } | ||
|
|
||
| Duration::from_secs(ret as u64) | ||
| } | ||
|
|
||
| /// Return a pseudorandom 64-bit float, chosen uniformly from the range [0.0, 1.0). | ||
| pub fn c_tor_crypto_rand_double() -> f64 { | ||
| unsafe { | ||
| crypto_rand_double() | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod test { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_layout_tor_weak_rng_t() { | ||
| assert_eq!(::std::mem::size_of::<tor_weak_rng_t>(), 0usize, | ||
| concat!("Size of: ", stringify!(tor_weak_rng_t))); | ||
| assert_eq!(::std::mem::align_of::<tor_weak_rng_t>(), 1usize, | ||
| concat!("Alignment of ", stringify!(tor_weak_rng_t))); | ||
| } | ||
| } | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,30 @@ | ||
| # TODO: Note that this package should be merged into the "crypto" crate after #24659 is merged. | ||
|
|
||
| [package] | ||
| authors = ["The Tor Project"] | ||
| version = "0.0.1" | ||
| name = "rand" | ||
|
|
||
| [features] | ||
| testing = ["tor_log/testing"] | ||
|
|
||
| [dependencies] | ||
| libc = "=0.2.39" | ||
| rand_core = "=0.1.0-pre.0" | ||
|
|
||
| [dependencies.external] | ||
| path = "../external" | ||
|
|
||
| [dependencies.tor_util] | ||
| path = "../tor_util" | ||
|
|
||
| [dependencies.tor_allocate] | ||
| path = "../tor_allocate" | ||
|
|
||
| [dependencies.tor_log] | ||
| path = "../tor_log" | ||
|
|
||
| [lib] | ||
| name = "rand" | ||
| path = "lib.rs" | ||
| crate_type = ["rlib", "staticlib"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| @@ -0,0 +1,15 @@ | ||
| // Copyright (c) 2018, The Tor Project, Inc. | ||
| // Copyright (c) 2018, isis agora lovecruft | ||
| // See LICENSE for licensing information | ||
|
|
||
| // External dependencies | ||
| extern crate rand_core; | ||
|
|
||
| // Internal dependencies | ||
| extern crate external; | ||
| #[cfg(not(test))] | ||
| #[macro_use] | ||
| extern crate tor_log; | ||
|
|
||
| pub mod rng; | ||
| pub mod prng; |
Oops, something went wrong.
This scares me. When we use smartlist_shuffle() or smartlist_choose() to access something, it doesn't need to be a smartlist-of-string, but smartlist-of-string is (I think!) what Stringlist guarantees.
Personally, I would rather just not expose smartlist_choose or smartlist_shuffle that to contaminate our Rust with this kind of C-ism.