[tor-commits] [tor/master] rust: Add crypto crate and implement Rust wrappers for SHA2 code.

nickm at torproject.org nickm at torproject.org
Tue May 8 22:47:55 UTC 2018


commit af182d4ab51d6a1a70559bbdcd4ab842aa855684
Author: Isis Lovecruft <isis at torproject.org>
Date:   Sat Apr 21 01:01:04 2018 +0000

    rust: Add crypto crate and implement Rust wrappers for SHA2 code.
    
     * FIXES #24659: https://bugs.torproject.org/24659
---
 configure.ac                       |   5 +-
 src/common/crypto_digest.c         |  15 +-
 src/rust/Cargo.lock                |  35 ++++
 src/rust/Cargo.toml                |  11 +-
 src/rust/crypto/Cargo.toml         |  21 ++
 src/rust/crypto/digests/mod.rs     |   7 +
 src/rust/crypto/digests/sha2.rs    | 213 ++++++++++++++++++++
 src/rust/crypto/lib.rs             |  36 ++++
 src/rust/external/Cargo.toml       |   3 +
 src/rust/external/crypto_digest.rs | 402 +++++++++++++++++++++++++++++++++++++
 src/rust/external/lib.rs           |   4 +-
 src/rust/include.am                |   5 +
 12 files changed, 752 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac
index 387fc6fbe..3bf436203 100644
--- a/configure.ac
+++ b/configure.ac
@@ -306,7 +306,10 @@ fi
 AM_CONDITIONAL(USEPYTHON, [test "x$PYTHON" != "x"])
 
 dnl List all external rust crates we depend on here. Include the version
-rust_crates="libc-0.2.39"
+rust_crates=" \
+    digest-0.7.2 \
+    libc-0.2.39 \
+"
 AC_SUBST(rust_crates)
 
 ifdef([AC_C_FLEXIBLE_ARRAY_MEMBER], [
diff --git a/src/common/crypto_digest.c b/src/common/crypto_digest.c
index f7163de13..9f9a1a1e2 100644
--- a/src/common/crypto_digest.c
+++ b/src/common/crypto_digest.c
@@ -268,7 +268,11 @@ crypto_digest_new(void)
 }
 
 /** Allocate and return a new digest object to compute 256-bit digests
- * using <b>algorithm</b>. */
+ * using <b>algorithm</b>.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest256_new`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::default`
+ */
 crypto_digest_t *
 crypto_digest256_new(digest_algorithm_t algorithm)
 {
@@ -298,6 +302,9 @@ crypto_digest_free_(crypto_digest_t *digest)
 }
 
 /** Add <b>len</b> bytes from <b>data</b> to the digest object.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_add_bytess`
+ * C_RUST_COUPLED: `crypto::digest::Sha256::process`
  */
 void
 crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
@@ -335,6 +342,9 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
 /** Compute the hash of the data that has been passed to the digest
  * object; write the first out_len bytes of the result to <b>out</b>.
  * <b>out_len</b> must be \<= DIGEST512_LEN.
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_get_digest`
+ * C_RUST_COUPLED: `impl digest::FixedOutput for Sha256`
  */
 void
 crypto_digest_get_digest(crypto_digest_t *digest,
@@ -383,6 +393,9 @@ crypto_digest_get_digest(crypto_digest_t *digest,
 
 /** Allocate and return a new digest object with the same state as
  * <b>digest</b>
+ *
+ * C_RUST_COUPLED: `external::crypto_digest::crypto_digest_dup`
+ * C_RUST_COUPLED: `impl Clone for crypto::digest::Sha256`
  */
 crypto_digest_t *
 crypto_digest_dup(const crypto_digest_t *digest)
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 2ac32809d..ddbc0ac2b 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -1,8 +1,35 @@
 [[package]]
+name = "crypto"
+version = "0.0.1"
+dependencies = [
+ "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "external 0.0.1",
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smartlist 0.0.1",
+]
+
+[[package]]
+name = "digest"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "external"
 version = "0.0.1"
 dependencies = [
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smartlist 0.0.1",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -87,7 +114,15 @@ dependencies = [
  "tor_log 0.1.0",
 ]
 
+[[package]]
+name = "typenum"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [metadata]
+"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
+"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
 "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
 "checksum rand 0.5.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7d7a7728c20bfd9fcc6e713e748e787c3d00e5ffd139b3ad1b5be92c5dfbaad5"
 "checksum rand_core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0224284424a4b818387b58d59336c288f99b48f69681aa60cc681fe038bbca5d"
+"checksum typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a99dc6780ef33c78780b826cf9d2a78840b72cae9474de4bcaf9051e60ebbd"
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index d47cd6422..1aaab0c4f 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,7 +1,14 @@
 [workspace]
-members = ["tor_util", "protover", "smartlist", "external", "tor_allocate",
-"tor_rust", "tor_log",
+members = [
+    "crypto",
+    "external",
+    "protover",
     "rand",
+    "smartlist",
+    "tor_allocate",
+    "tor_log",
+    "tor_rust",
+    "tor_util",
 ]
 
 [profile.release]
diff --git a/src/rust/crypto/Cargo.toml b/src/rust/crypto/Cargo.toml
new file mode 100644
index 000000000..e6a8bffa2
--- /dev/null
+++ b/src/rust/crypto/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+authors = ["The Tor Project",
+           "Isis Lovecruft <isis at torproject.org>"]
+name = "crypto"
+version = "0.0.1"
+publish = false
+
+[lib]
+name = "crypto"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
+[dependencies]
+libc = "=0.2.39"
+digest = "=0.7.2"
+
+[dependencies.external]
+path = "../external"
+
+[dependencies.smartlist]
+path = "../smartlist"
diff --git a/src/rust/crypto/digests/mod.rs b/src/rust/crypto/digests/mod.rs
new file mode 100644
index 000000000..a2463b89e
--- /dev/null
+++ b/src/rust/crypto/digests/mod.rs
@@ -0,0 +1,7 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Hash Digests and eXtendible Output Functions (XOFs)
+
+pub mod sha2;
diff --git a/src/rust/crypto/digests/sha2.rs b/src/rust/crypto/digests/sha2.rs
new file mode 100644
index 000000000..1cbb6c581
--- /dev/null
+++ b/src/rust/crypto/digests/sha2.rs
@@ -0,0 +1,213 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Hash Digests and eXtendible Output Functions (XOFs)
+
+pub use digest::Digest;
+
+use digest::BlockInput;
+use digest::FixedOutput;
+use digest::Input;
+use digest::generic_array::GenericArray;
+use digest::generic_array::typenum::U32;
+use digest::generic_array::typenum::U64;
+
+use external::crypto_digest::CryptoDigest;
+use external::crypto_digest::DigestAlgorithm;
+use external::crypto_digest::get_256_bit_digest;
+use external::crypto_digest::get_512_bit_digest;
+
+pub use external::crypto_digest::DIGEST256_LEN;
+pub use external::crypto_digest::DIGEST512_LEN;
+
+/// The block size for both SHA-256 and SHA-512 digests is 512 bits/64 bytes.
+///
+/// Unfortunately, we have to use the generic_array crate currently to express
+/// this at compile time.  Later, in the future, when Rust implements const
+/// generics, we'll be able to remove this dependency (actually, it will get
+/// removed from the digest crate, which is currently `pub use`ing it).
+type BlockSize = U64;
+
+/// A SHA2-256 digest.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+#[derive(Clone)]
+pub struct Sha256 {
+    engine: CryptoDigest,
+}
+
+/// Construct a new, default instance of a `Sha256` hash digest function.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha256;
+///
+/// let hasher: Sha256 = Sha256::default();
+/// ```
+///
+/// # Returns
+///
+/// A new `Sha256` digest.
+impl Default for Sha256 {
+    fn default() -> Sha256 {
+        Sha256{ engine: CryptoDigest::new(Some(DigestAlgorithm::SHA2_256)) }
+    }
+}
+
+impl BlockInput for Sha256 {
+    type BlockSize = BlockSize;
+}
+
+/// Input `msg` into the digest.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha256;
+///
+/// let hasher: Sha256 = Sha256::default();
+///
+/// hasher.process(b"foo");
+/// hasher.process(b"bar");
+/// ```
+impl Input for Sha256 {
+    fn process(&mut self, msg: &[u8]) {
+        self.engine.add_bytes(&msg);
+    }
+}
+
+/// Retrieve the output hash from everything which has been fed into this
+/// `Sha256` digest thus far.
+///
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest in external::crypto_digest.
+impl FixedOutput for Sha256 {
+    type OutputSize = U32;
+
+    fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
+        let buffer: [u8; DIGEST256_LEN] = get_256_bit_digest(self.engine);
+
+        GenericArray::from(buffer)
+    }
+}
+
+/// A SHA2-512 digest.
+///
+/// # C_RUST_COUPLED
+///
+/// * `crypto_digest_dup`
+#[derive(Clone)]
+pub struct Sha512 {
+    engine: CryptoDigest,
+}
+
+/// Construct a new, default instance of a `Sha512` hash digest function.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha512;
+///
+/// let hasher: Sha256 = Sha512::default();
+/// ```
+///
+/// # Returns
+///
+/// A new `Sha512` digest.
+impl Default for Sha512 {
+    fn default() -> Sha512 {
+        Sha512{ engine: CryptoDigest::new(Some(DigestAlgorithm::SHA2_512)) }
+    }
+}
+
+impl BlockInput for Sha512 {
+    type BlockSize = BlockSize;
+}
+
+/// Input `msg` into the digest.
+///
+/// # Examples
+///
+/// ```
+/// use crypto::digest::Sha512;
+///
+/// let hasher: Sha512 = Sha512::default();
+///
+/// hasher.process(b"foo");
+/// hasher.process(b"bar");
+/// ```
+impl Input for Sha512 {
+    fn process(&mut self, msg: &[u8]) {
+        self.engine.add_bytes(&msg);
+    }
+}
+
+/// Retrieve the output hash from everything which has been fed into this
+/// `Sha512` digest thus far.
+///
+//
+// FIXME: Once const generics land in Rust, we should genericise calling
+// crypto_digest_get_digest in external::crypto_digest.
+impl FixedOutput for Sha512 {
+    type OutputSize = U32;
+
+    fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
+        let buffer: [u8; DIGEST512_LEN] = get_512_bit_digest(self.engine);
+
+        GenericArray::clone_from_slice(&buffer)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use digest::Digest;
+
+    use super::*;
+
+    #[test]
+    fn sha256_default() {
+        let _: Sha256 = Sha256::default();
+    }
+
+    #[test]
+    fn sha256_digest() {
+        let mut h: Sha256 = Sha256::new();
+        let mut result: [u8; DIGEST256_LEN] = [0u8; DIGEST256_LEN];
+
+        h.input(b"foo");
+        h.input(b"bar");
+        h.input(b"baz");
+
+        result.copy_from_slice(h.fixed_result().as_slice());
+
+        println!("{:?}", &result[..]);
+
+        assert_eq!(&result[..], &b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[..]);
+    }
+
+    #[test]
+    fn sha512_default() {
+        let _: Sha512 = Sha512::default();
+    }
+
+    #[test]
+    fn sha512_digest() {
+        let mut h: Sha512 = Sha512::new();
+        let mut result: [u8; DIGEST512_LEN] = [0u8; DIGEST512_LEN];
+
+        h.input(b"foo");
+        h.input(b"bar");
+        h.input(b"baz");
+
+        result.copy_from_slice(h.fixed_result().as_slice());
+
+        println!("{:?}", &result[..]);
+
+        assert_eq!(&result[..], &b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"[..]);
+    }
+}
diff --git a/src/rust/crypto/lib.rs b/src/rust/crypto/lib.rs
new file mode 100644
index 000000000..d0793a857
--- /dev/null
+++ b/src/rust/crypto/lib.rs
@@ -0,0 +1,36 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Common cryptographic functions and utilities.
+//!
+//! # Hash Digests and eXtendable Output Functions (XOFs)
+//!
+//! The `digests` module contains submodules for specific hash digests
+//! and extendable output functions.
+//!
+//! ```
+//! use crypto::digests::sha256::Sha256;
+//!
+//! let hasher: Sha256 = Sha256::default();
+//! let mut result: [u8; 32] = [0u8; 32];
+//!
+//! hasher.input("foo");
+//! hasher.input("bar");
+//! hasher.input("baz");
+//!
+//! result.copy_from_slice(hasher.result().as_bytes());
+//!
+//! assert!(result == "XXX");
+//! ```
+
+#[deny(missing_docs)]
+
+// External crates from cargo or TOR_RUST_DEPENDENCIES.
+extern crate digest;
+extern crate libc;
+
+// Our local crates.
+extern crate external;
+
+mod digests;  // Unfortunately named "digests" plural to avoid name conflict with the digest crate
diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml
index b5957b107..60ec03be4 100644
--- a/src/rust/external/Cargo.toml
+++ b/src/rust/external/Cargo.toml
@@ -6,6 +6,9 @@ name = "external"
 [dependencies]
 libc = "=0.2.39"
 
+[dependencies.smartlist]
+path = "../smartlist"
+
 [lib]
 name = "external"
 path = "lib.rs"
diff --git a/src/rust/external/crypto_digest.rs b/src/rust/external/crypto_digest.rs
new file mode 100644
index 000000000..bc49e6124
--- /dev/null
+++ b/src/rust/external/crypto_digest.rs
@@ -0,0 +1,402 @@
+// Copyright (c) 2018, 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 total number of digest algorithms we currently support.
+///
+/// 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_DIGEST_ALGORITHMS: usize = DIGEST_SHA3_512 as usize + 1;
+
+/// 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)]
+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)
+        }
+    }
+}
+
+/// 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/lib.rs b/src/rust/external/lib.rs
index 5fd74cf4c..ffd38ac5d 100644
--- a/src/rust/external/lib.rs
+++ b/src/rust/external/lib.rs
@@ -9,8 +9,10 @@
 
 extern crate libc;
 
+extern crate smartlist;
+
+pub mod crypto_digest;
 mod crypto_rand;
 mod external;
 
-pub use crypto_rand::*;
 pub use external::*;
diff --git a/src/rust/include.am b/src/rust/include.am
index 3edc87e1e..ba652bda0 100644
--- a/src/rust/include.am
+++ b/src/rust/include.am
@@ -4,7 +4,12 @@ EXTRA_DIST +=\
 	src/rust/Cargo.toml \
 	src/rust/Cargo.lock \
 	src/rust/.cargo/config.in \
+	src/rust/crypto/Cargo.toml \
+	src/rust/crypto/lib.rs \
+	src/rust/crypto/digests/mod.rs \
+	src/rust/crypto/digests/sha2.rs \
 	src/rust/external/Cargo.toml \
+	src/rust/external/crypto_digest.rs \
 	src/rust/external/crypto_rand.rs \
 	src/rust/external/external.rs \
 	src/rust/external/lib.rs \



More information about the tor-commits mailing list