Skip to content
Permalink
Browse files
rust: Expose our (P)RNGs in Rust and provide safe wrappers.
  • Loading branch information
isislovecruft committed Apr 11, 2018
1 parent 809f6fa commit 5a984badc59927063d9682ffd8cc6f52fc15a44b
Showing with 538 additions and 2 deletions.
  1. +19 −0 src/rust/Cargo.lock
  2. +3 −1 src/rust/Cargo.toml
  3. +3 −0 src/rust/external/Cargo.toml
  4. +177 −0 src/rust/external/crypto_rand.rs
  5. +5 −1 src/rust/external/lib.rs
  6. +30 −0 src/rust/rand/Cargo.toml
  7. +15 −0 src/rust/rand/lib.rs
  8. +184 −0 src/rust/rand/prng.rs
  9. +95 −0 src/rust/rand/rng.rs
  10. +7 −0 src/rust/rand/src/lib.rs

Some generated files are not rendered by default. Learn more.

@@ -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
@@ -6,6 +6,9 @@ name = "external"
[dependencies]
libc = "=0.2.39"

[dependencies.smartlist]
path = "../smartlist"

[lib]
name = "external"
path = "lib.rs"
@@ -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.

Copy link
@nmathewson

nmathewson Apr 16, 2018

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.

This comment has been minimized.

Copy link
@isislovecruft

isislovecruft Apr 18, 2018

Author Owner

Ah, oops, that should have been removed, since I also commented out smartlist_choose and smartlist_shuffle below.

This comment has been minimized.

Copy link
@isislovecruft

isislovecruft Apr 18, 2018

Author Owner

Fixed in 9f5ef23


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.

Copy link
@nmathewson

nmathewson Apr 16, 2018

Are there regular Rust APIs that use "bool" to indicate succeeded/failed?

If not, let's use Result<(),something>.

This comment has been minimized.

Copy link
@isislovecruft

isislovecruft Apr 18, 2018

Author Owner

I thought the external crate was meant to be very C-ish interfaces to directly call the C, and then more Rusty types were supposed to live in their own modules?

This comment has been minimized.

Copy link
@nmathewson

nmathewson Apr 22, 2018

oh! Yes, sorry. I must've misunderstood where this code was.

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.

Copy link
@nmathewson

nmathewson Apr 16, 2018

This cannot possibly be the standard term.

Let's not be That Project.

This comment has been minimized.

Copy link
@isislovecruft

isislovecruft Apr 18, 2018

Author Owner

Changed to "# Aborts" in 6d6718b

This comment has been minimized.

Copy link
@nmathewson
///
/// 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.

Copy link
@nmathewson

nmathewson Apr 16, 2018

Design question: Should we expose any of these functions besides crypto_rand and crypto_strongest_rand? It seems that in Rust, we're always better off just implementing Rng with those functions, and using the Rust versions of the functions to generate other things.

This comment has been minimized.

Copy link
@isislovecruft

isislovecruft Apr 20, 2018

Author Owner

Yes, that's probably a better idea! I've gone ahead and done this in e47db10, 9fa8fb7, and 0dbe618

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)));
}
}
@@ -1,4 +1,4 @@
//! Copyright (c) 2016-2017, The Tor Project, Inc. */
//! Copyright (c) 2016-2018, The Tor Project, Inc. */
//! See LICENSE for licensing information */

//! Interface for external calls to tor C ABI
@@ -9,6 +9,10 @@

extern crate libc;

extern crate smartlist;

mod crypto_rand;
mod external;

pub use crypto_rand::*;
pub use external::*;
@@ -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"]
@@ -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;

0 comments on commit 5a984ba

Please sign in to comment.