aboutsummaryrefslogtreecommitdiff
path: root/src/rust/external
diff options
context:
space:
mode:
Diffstat (limited to 'src/rust/external')
-rw-r--r--src/rust/external/Cargo.toml20
-rw-r--r--src/rust/external/crypto_digest.rs454
-rw-r--r--src/rust/external/crypto_rand.rs84
-rw-r--r--src/rust/external/external.rs37
-rw-r--r--src/rust/external/lib.rs19
5 files changed, 614 insertions, 0 deletions
diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml
new file mode 100644
index 0000000000..5f443645bb
--- /dev/null
+++ b/src/rust/external/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+authors = ["The Tor Project"]
+version = "0.0.1"
+name = "external"
+
+[dependencies]
+libc = "=0.2.39"
+smartlist = { path = "../smartlist" }
+tor_allocate = { path = "../tor_allocate" }
+
+[lib]
+name = "external"
+path = "lib.rs"
+
+[features]
+# We have to define a feature here because doctests don't get cfg(test),
+# and we need to disable some C dependencies when running the doctests
+# because of the various linker issues. See
+# https://github.com/rust-lang/rust/issues/45599
+test_linking_hack = []
diff --git a/src/rust/external/crypto_digest.rs b/src/rust/external/crypto_digest.rs
new file mode 100644
index 0000000000..454f836bad
--- /dev/null
+++ b/src/rust/external/crypto_digest.rs
@@ -0,0 +1,454 @@
+// Copyright (c) 2018-2019, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Bindings to external digest and XOF functions which live within
+//! src/common/crypto_digest.[ch].
+//!
+//! We wrap our C implementations in src/common/crypto_digest.[ch] with more
+//! Rusty types and interfaces in src/rust/crypto/digest/.
+
+use std::process::abort;
+
+use libc::c_char;
+use libc::c_int;
+use libc::size_t;
+use libc::uint8_t;
+
+use smartlist::Stringlist;
+
+/// Length of the output of our message digest.
+pub const DIGEST_LEN: usize = 20;
+
+/// Length of the output of our second (improved) message digests. (For now
+/// this is just sha256, but it could be any other 256-bit digest.)
+pub const DIGEST256_LEN: usize = 32;
+
+/// Length of the output of our 64-bit optimized message digests (SHA512).
+pub const DIGEST512_LEN: usize = 64;
+
+/// Length of a sha1 message digest when encoded in base32 with trailing = signs
+/// removed.
+pub const BASE32_DIGEST_LEN: usize = 32;
+
+/// Length of a sha1 message digest when encoded in base64 with trailing = signs
+/// removed.
+pub const BASE64_DIGEST_LEN: usize = 27;
+
+/// Length of a sha256 message digest when encoded in base64 with trailing =
+/// signs removed.
+pub const BASE64_DIGEST256_LEN: usize = 43;
+
+/// Length of a sha512 message digest when encoded in base64 with trailing =
+/// signs removed.
+pub const BASE64_DIGEST512_LEN: usize = 86;
+
+/// Length of hex encoding of SHA1 digest, not including final NUL.
+pub const HEX_DIGEST_LEN: usize = 40;
+
+/// Length of hex encoding of SHA256 digest, not including final NUL.
+pub const HEX_DIGEST256_LEN: usize = 64;
+
+/// Length of hex encoding of SHA512 digest, not including final NUL.
+pub const HEX_DIGEST512_LEN: usize = 128;
+
+/// Our C code uses an enum to declare the digest algorithm types which we know
+/// about. However, because enums are implementation-defined in C, we can
+/// neither work with them directly nor translate them into Rust enums.
+/// Instead, we represent them as a u8 (under the assumption that we'll never
+/// support more than 256 hash functions).
+#[allow(non_camel_case_types)]
+type digest_algorithm_t = u8;
+
+const DIGEST_SHA1: digest_algorithm_t = 0;
+const DIGEST_SHA256: digest_algorithm_t = 1;
+const DIGEST_SHA512: digest_algorithm_t = 2;
+const DIGEST_SHA3_256: digest_algorithm_t = 3;
+const DIGEST_SHA3_512: digest_algorithm_t = 4;
+
+/// The number of hash digests we produce for a `common_digests_t`.
+///
+/// We can't access these from Rust, because their definitions in C require
+/// introspecting the `digest_algorithm_t` typedef, which is an enum, so we have
+/// to redefine them here.
+const N_COMMON_DIGEST_ALGORITHMS: usize = DIGEST_SHA256 as usize + 1;
+
+/// A digest function.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct crypto_digest_t {
+ // This private, zero-length field forces the struct to be treated the same
+ // as its opaque C couterpart.
+ _unused: [u8; 0],
+}
+
+/// An eXtendible Output Function (XOF).
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct crypto_xof_t {
+ // This private, zero-length field forces the struct to be treated the same
+ // as its opaque C couterpart.
+ _unused: [u8; 0],
+}
+
+/// A set of all the digests we commonly compute, taken on a single
+/// string. Any digests that are shorter than 512 bits are right-padded
+/// with 0 bits.
+///
+/// Note that this representation wastes 44 bytes for the SHA1 case, so
+/// don't use it for anything where we need to allocate a whole bunch at
+/// once.
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+#[allow(non_camel_case_types)]
+struct common_digests_t {
+ pub d: [[c_char; N_COMMON_DIGEST_ALGORITHMS]; DIGEST256_LEN],
+}
+
+/// 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)]
+// BINDGEN_GENERATED: This type isn't actually bindgen generated, but the code
+// below it which uses it is. As such, this comes up as "dead code" as well.
+#[allow(dead_code)]
+type smartlist_t = Stringlist;
+
+/// All of the external functions from `src/common/crypto_digest.h`.
+///
+/// These are kept private because they should be wrapped with Rust to make their usage safer.
+//
+// BINDGEN_GENERATED: These definitions were generated with bindgen and cleaned
+// up manually. As such, there are more bindings than are likely necessary or
+// which are in use.
+#[allow(dead_code)]
+extern "C" {
+ fn crypto_digest(digest: *mut c_char, m: *const c_char, len: size_t) -> c_int;
+ fn crypto_digest256(
+ digest: *mut c_char,
+ m: *const c_char,
+ len: size_t,
+ algorithm: digest_algorithm_t,
+ ) -> c_int;
+ fn crypto_digest512(
+ digest: *mut c_char,
+ m: *const c_char,
+ len: size_t,
+ algorithm: digest_algorithm_t,
+ ) -> c_int;
+ fn crypto_common_digests(ds_out: *mut common_digests_t, m: *const c_char, len: size_t)
+ -> c_int;
+ fn crypto_digest_smartlist_prefix(
+ digest_out: *mut c_char,
+ len_out: size_t,
+ prepend: *const c_char,
+ lst: *const smartlist_t,
+ append: *const c_char,
+ alg: digest_algorithm_t,
+ );
+ fn crypto_digest_smartlist(
+ digest_out: *mut c_char,
+ len_out: size_t,
+ lst: *const smartlist_t,
+ append: *const c_char,
+ alg: digest_algorithm_t,
+ );
+ fn crypto_digest_algorithm_get_name(alg: digest_algorithm_t) -> *const c_char;
+ fn crypto_digest_algorithm_get_length(alg: digest_algorithm_t) -> size_t;
+ fn crypto_digest_algorithm_parse_name(name: *const c_char) -> c_int;
+ fn crypto_digest_new() -> *mut crypto_digest_t;
+ fn crypto_digest256_new(algorithm: digest_algorithm_t) -> *mut crypto_digest_t;
+ fn crypto_digest512_new(algorithm: digest_algorithm_t) -> *mut crypto_digest_t;
+ fn crypto_digest_free_(digest: *mut crypto_digest_t);
+ fn crypto_digest_add_bytes(digest: *mut crypto_digest_t, data: *const c_char, len: size_t);
+ fn crypto_digest_get_digest(digest: *mut crypto_digest_t, out: *mut c_char, out_len: size_t);
+ fn crypto_digest_dup(digest: *const crypto_digest_t) -> *mut crypto_digest_t;
+ fn crypto_digest_assign(into: *mut crypto_digest_t, from: *const crypto_digest_t);
+ fn crypto_hmac_sha256(
+ hmac_out: *mut c_char,
+ key: *const c_char,
+ key_len: size_t,
+ msg: *const c_char,
+ msg_len: size_t,
+ );
+ fn crypto_mac_sha3_256(
+ mac_out: *mut uint8_t,
+ len_out: size_t,
+ key: *const uint8_t,
+ key_len: size_t,
+ msg: *const uint8_t,
+ msg_len: size_t,
+ );
+ fn crypto_xof_new() -> *mut crypto_xof_t;
+ fn crypto_xof_add_bytes(xof: *mut crypto_xof_t, data: *const uint8_t, len: size_t);
+ fn crypto_xof_squeeze_bytes(xof: *mut crypto_xof_t, out: *mut uint8_t, len: size_t);
+ fn crypto_xof_free(xof: *mut crypto_xof_t);
+}
+
+/// A wrapper around a `digest_algorithm_t`.
+pub enum DigestAlgorithm {
+ SHA2_256,
+ SHA2_512,
+ SHA3_256,
+ SHA3_512,
+}
+
+impl From<DigestAlgorithm> for digest_algorithm_t {
+ fn from(digest: DigestAlgorithm) -> digest_algorithm_t {
+ match digest {
+ DigestAlgorithm::SHA2_256 => DIGEST_SHA256,
+ DigestAlgorithm::SHA2_512 => DIGEST_SHA512,
+ DigestAlgorithm::SHA3_256 => DIGEST_SHA3_256,
+ DigestAlgorithm::SHA3_512 => DIGEST_SHA3_512,
+ }
+ }
+}
+
+/// A wrapper around a mutable pointer to a `crypto_digest_t`.
+pub struct CryptoDigest(*mut crypto_digest_t);
+
+/// Explicitly copy the state of a `CryptoDigest` hash digest context.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+impl Clone for CryptoDigest {
+ fn clone(&self) -> CryptoDigest {
+ let digest: *mut crypto_digest_t;
+
+ unsafe {
+ digest = crypto_digest_dup(self.0 as *const crypto_digest_t);
+ }
+
+ // See the note in the implementation of CryptoDigest for the
+ // reasoning for `abort()` here.
+ if digest.is_null() {
+ abort();
+ }
+
+ CryptoDigest(digest)
+ }
+}
+
+impl CryptoDigest {
+ /// A wrapper to call one of the C functions `crypto_digest_new`,
+ /// `crypto_digest256_new`, or `crypto_digest512_new`.
+ ///
+ /// # Warnings
+ ///
+ /// This function will `abort()` the entire process in an "abnormal" fashion,
+ /// i.e. not unwinding this or any other thread's stack, running any
+ /// destructors, or calling any panic/exit hooks) if `tor_malloc()` (called in
+ /// `crypto_digest256_new()`) is unable to allocate memory.
+ ///
+ /// # Returns
+ ///
+ /// A new `CryptoDigest`, which is a wrapper around a opaque representation
+ /// of a `crypto_digest_t`. The underlying `crypto_digest_t` _MUST_ only
+ /// ever be handled via a raw pointer, and never introspected.
+ ///
+ /// # C_RUST_COUPLED
+ ///
+ /// * `crypto_digest_new`
+ /// * `crypto_digest256_new`
+ /// * `crypto_digest512_new`
+ /// * `tor_malloc` (called by `crypto_digest256_new`, but we make
+ /// assumptions about its behvaiour and return values here)
+ pub fn new(algorithm: Option<DigestAlgorithm>) -> CryptoDigest {
+ let digest: *mut crypto_digest_t;
+
+ if algorithm.is_none() {
+ unsafe {
+ digest = crypto_digest_new();
+ }
+ } else {
+ let algo: digest_algorithm_t = algorithm.unwrap().into(); // can't fail because it's Some
+
+ unsafe {
+ // XXX This is a pretty awkward API to use from Rust...
+ digest = match algo {
+ DIGEST_SHA1 => crypto_digest_new(),
+ DIGEST_SHA256 => crypto_digest256_new(DIGEST_SHA256),
+ DIGEST_SHA3_256 => crypto_digest256_new(DIGEST_SHA3_256),
+ DIGEST_SHA512 => crypto_digest512_new(DIGEST_SHA512),
+ DIGEST_SHA3_512 => crypto_digest512_new(DIGEST_SHA3_512),
+ _ => abort(),
+ }
+ }
+ }
+
+ // In our C code, `crypto_digest*_new()` allocates memory with
+ // `tor_malloc()`. In `tor_malloc()`, if the underlying malloc
+ // implementation fails to allocate the requested memory and returns a
+ // NULL pointer, we call `exit(1)`. In the case that this `exit(1)` is
+ // called within a worker, be that a process or a thread, the inline
+ // comments within `tor_malloc()` mention "that's ok, since the parent
+ // will run out of memory soon anyway". However, if it takes long
+ // enough for the worker to die, and it manages to return a NULL pointer
+ // to our Rust code, our Rust is now in an irreparably broken state and
+ // may exhibit undefined behaviour. An even worse scenario, if/when we
+ // have parent/child processes/threads controlled by Rust, would be that
+ // the UB contagion in Rust manages to spread to other children before
+ // the entire process (hopefully terminates).
+ //
+ // However, following the assumptions made in `tor_malloc()` that
+ // calling `exit(1)` in a child is okay because the parent will
+ // eventually run into the same errors, and also to stymie any UB
+ // contagion in the meantime, we call abort!() here to terminate the
+ // entire program immediately.
+ if digest.is_null() {
+ abort();
+ }
+
+ CryptoDigest(digest)
+ }
+
+ /// A wrapper to call the C function `crypto_digest_add_bytes`.
+ ///
+ /// # Inputs
+ ///
+ /// * `bytes`: a byte slice of bytes to be added into this digest.
+ ///
+ /// # C_RUST_COUPLED
+ ///
+ /// * `crypto_digest_add_bytes`
+ pub fn add_bytes(&self, bytes: &[u8]) {
+ unsafe {
+ crypto_digest_add_bytes(
+ self.0 as *mut crypto_digest_t,
+ bytes.as_ptr() as *const c_char,
+ bytes.len() as size_t,
+ )
+ }
+ }
+}
+
+impl Drop for CryptoDigest {
+ fn drop(&mut self) {
+ unsafe {
+ crypto_digest_free_(self.0 as *mut crypto_digest_t);
+ }
+ }
+}
+
+/// Get the 256-bit digest output of a `crypto_digest_t`.
+///
+/// # Inputs
+///
+/// * `digest`: A `CryptoDigest` which wraps either a `DIGEST_SHA256` or a
+/// `DIGEST_SHA3_256`.
+///
+/// # Warning
+///
+/// Calling this function with a `CryptoDigest` which is neither SHA2-256 or
+/// SHA3-256 is a programming error. Since we cannot introspect the opaque
+/// struct from Rust, however, there is no way for us to check that the correct
+/// one is being passed in. That is up to you, dear programmer. If you mess
+/// up, you will get a incorrectly-sized hash digest in return, and it will be
+/// your fault. Don't do that.
+///
+/// # Returns
+///
+/// A 256-bit hash digest, as a `[u8; 32]`.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_get_digest`
+/// * `DIGEST256_LEN`
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest w.r.t. output array size.
+pub fn get_256_bit_digest(digest: CryptoDigest) -> [u8; DIGEST256_LEN] {
+ let mut buffer: [u8; DIGEST256_LEN] = [0u8; DIGEST256_LEN];
+
+ unsafe {
+ crypto_digest_get_digest(
+ digest.0,
+ buffer.as_mut_ptr() as *mut c_char,
+ DIGEST256_LEN as size_t,
+ );
+
+ if buffer.as_ptr().is_null() {
+ abort();
+ }
+ }
+ buffer
+}
+
+/// Get the 512-bit digest output of a `crypto_digest_t`.
+///
+/// # Inputs
+///
+/// * `digest`: A `CryptoDigest` which wraps either a `DIGEST_SHA512` or a
+/// `DIGEST_SHA3_512`.
+///
+/// # Warning
+///
+/// Calling this function with a `CryptoDigest` which is neither SHA2-512 or
+/// SHA3-512 is a programming error. Since we cannot introspect the opaque
+/// struct from Rust, however, there is no way for us to check that the correct
+/// one is being passed in. That is up to you, dear programmer. If you mess
+/// up, you will get a incorrectly-sized hash digest in return, and it will be
+/// your fault. Don't do that.
+///
+/// # Returns
+///
+/// A 512-bit hash digest, as a `[u8; 64]`.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_get_digest`
+/// * `DIGEST512_LEN`
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest w.r.t. output array size.
+pub fn get_512_bit_digest(digest: CryptoDigest) -> [u8; DIGEST512_LEN] {
+ let mut buffer: [u8; DIGEST512_LEN] = [0u8; DIGEST512_LEN];
+
+ unsafe {
+ crypto_digest_get_digest(
+ digest.0,
+ buffer.as_mut_ptr() as *mut c_char,
+ DIGEST512_LEN as size_t,
+ );
+
+ if buffer.as_ptr().is_null() {
+ abort();
+ }
+ }
+ buffer
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_layout_common_digests_t() {
+ assert_eq!(
+ ::std::mem::size_of::<common_digests_t>(),
+ 64usize,
+ concat!("Size of: ", stringify!(common_digests_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<common_digests_t>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(common_digests_t))
+ );
+ }
+
+ #[test]
+ fn test_layout_crypto_digest_t() {
+ assert_eq!(
+ ::std::mem::size_of::<crypto_digest_t>(),
+ 0usize,
+ concat!("Size of: ", stringify!(crypto_digest_t))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<crypto_digest_t>(),
+ 1usize,
+ concat!("Alignment of ", stringify!(crypto_digest_t))
+ );
+ }
+}
diff --git a/src/rust/external/crypto_rand.rs b/src/rust/external/crypto_rand.rs
new file mode 100644
index 0000000000..703382093c
--- /dev/null
+++ b/src/rust/external/crypto_rand.rs
@@ -0,0 +1,84 @@
+// Copyright (c) 2018-2019, 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_double;
+use libc::c_int;
+use libc::size_t;
+use libc::time_t;
+use libc::uint8_t;
+
+extern "C" {
+ fn crypto_seed_rng() -> c_int;
+ fn crypto_rand(out: *mut uint8_t, out_len: size_t);
+ fn crypto_strongest_rand(out: *mut uint8_t, out_len: size_t);
+ fn crypto_rand_time_range(min: time_t, max: time_t) -> time_t;
+ fn crypto_rand_double() -> c_double;
+}
+
+/// 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 {
+ let ret: c_int;
+
+ unsafe {
+ ret = crypto_seed_rng();
+ }
+ match ret {
+ 0 => return true,
+ _ => return false,
+ }
+}
+
+/// Fill the bytes of `dest` with random data.
+pub fn c_tor_crypto_rand(dest: &mut [u8]) {
+ unsafe {
+ crypto_rand(dest.as_mut_ptr(), dest.len() as size_t);
+ }
+}
+
+/// Fill the bytes of `dest` with "strong" random data by hashing
+/// together randomness obtained from OpenSSL's RNG and the operating
+/// system.
+pub fn c_tor_crypto_strongest_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_strongest_rand(dest.as_mut_ptr(), dest.len() as size_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() }
+}
diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs
new file mode 100644
index 0000000000..0d324c8820
--- /dev/null
+++ b/src/rust/external/external.rs
@@ -0,0 +1,37 @@
+// Copyright (c) 2016-2019, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use libc::{c_char, c_int};
+use std::ffi::CString;
+
+extern "C" {
+ fn tor_version_as_new_as(platform: *const c_char, cutoff: *const c_char) -> c_int;
+}
+
+/// Wrap calls to tor_version_as_new_as, defined in routerparse.c
+pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool {
+ // CHK: These functions should log a warning if an error occurs. This
+ // can be added when integration with tor's logger is added to rust
+ let c_platform = match CString::new(platform) {
+ Ok(n) => n,
+ Err(_) => return false,
+ };
+
+ let c_cutoff = match CString::new(cutoff) {
+ Ok(n) => n,
+ Err(_) => return false,
+ };
+
+ let result: c_int = unsafe { tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()) };
+
+ result == 1
+}
+
+extern "C" {
+ fn tor_is_using_nss() -> c_int;
+}
+
+/// Return true if Tor was built to use NSS.
+pub fn c_tor_is_using_nss() -> bool {
+ 0 != unsafe { tor_is_using_nss() }
+}
diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs
new file mode 100644
index 0000000000..2f50610a4d
--- /dev/null
+++ b/src/rust/external/lib.rs
@@ -0,0 +1,19 @@
+//! Copyright (c) 2016-2019, The Tor Project, Inc. */
+//! See LICENSE for licensing information */
+
+//! Interface for external calls to tor C ABI
+//!
+//! The purpose of this module is to provide a clean interface for when Rust
+//! modules need to interact with functionality in tor C code rather than each
+//! module implementing this functionality repeatedly.
+
+extern crate libc;
+extern crate tor_allocate;
+extern crate smartlist;
+
+pub mod crypto_digest;
+mod crypto_rand;
+mod external;
+
+pub use crypto_rand::*;
+pub use external::*;