diff options
Diffstat (limited to 'src/rust/crypto')
-rw-r--r-- | src/rust/crypto/Cargo.toml | 28 | ||||
-rw-r--r-- | src/rust/crypto/digests/mod.rs | 7 | ||||
-rw-r--r-- | src/rust/crypto/digests/sha2.rs | 222 | ||||
-rw-r--r-- | src/rust/crypto/lib.rs | 45 | ||||
-rw-r--r-- | src/rust/crypto/rand/mod.rs | 6 | ||||
-rw-r--r-- | src/rust/crypto/rand/rng.rs | 140 |
6 files changed, 448 insertions, 0 deletions
diff --git a/src/rust/crypto/Cargo.toml b/src/rust/crypto/Cargo.toml new file mode 100644 index 0000000000..869e0d6256 --- /dev/null +++ b/src/rust/crypto/Cargo.toml @@ -0,0 +1,28 @@ +[package] +authors = ["The Tor Project", + "Isis Lovecruft <isis@torproject.org>"] +name = "crypto" +version = "0.0.1" +publish = false +build = "../build.rs" + +[lib] +name = "crypto" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies] +libc = "=0.2.39" +digest = "=0.7.2" +rand_core = { version = "=0.2.0-pre.0", default-features = false } + +external = { path = "../external" } +smartlist = { path = "../smartlist" } +tor_allocate = { path = "../tor_allocate" } +tor_log = { path = "../tor_log" } + +[dev-dependencies] +rand = { version = "=0.5.0-pre.2", default-features = false } +rand_core = { version = "=0.2.0-pre.0", default-features = false } + +[features] diff --git a/src/rust/crypto/digests/mod.rs b/src/rust/crypto/digests/mod.rs new file mode 100644 index 0000000000..a2463b89eb --- /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 0000000000..03e0843dc0 --- /dev/null +++ b/src/rust/crypto/digests/sha2.rs @@ -0,0 +1,222 @@ +// 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 +/// +/// ```rust,no_run +/// use crypto::digests::sha2::{Sha256, Digest}; +/// +/// let mut 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 +/// +/// ```rust,no_run +/// use crypto::digests::sha2::{Sha256, Digest}; +/// +/// let mut hasher: Sha256 = Sha256::default(); +/// +/// hasher.input(b"foo"); +/// hasher.input(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 +/// +/// ```rust,no_run +/// use crypto::digests::sha2::{Sha512, Digest}; +/// +/// let mut hasher: Sha512 = 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 +/// +/// ```rust,no_run +/// use crypto::digests::sha2::{Sha512, Digest}; +/// +/// let mut hasher: Sha512 = Sha512::default(); +/// +/// hasher.input(b"foo"); +/// hasher.input(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 = U64; + + 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]; + let expected = [151, 223, 53, 136, 181, 163, 242, 75, 171, 195, + 133, 27, 55, 47, 11, 167, 26, 157, 205, 222, 212, + 59, 20, 185, 208, 105, 97, 191, 193, 112, 125, 157]; + + 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, expected); + } + + #[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]; + + let expected = [203, 55, 124, 16, 176, 245, 166, 44, 128, 54, 37, 167, + 153, 217, 233, 8, 190, 69, 231, 103, 245, 209, 71, 212, 116, + 73, 7, 203, 5, 89, 122, 164, 237, 211, 41, 160, 175, 20, 122, + 221, 12, 244, 24, 30, 211, 40, 250, 30, 121, 148, 38, 88, 38, + 179, 237, 61, 126, 246, 240, 103, 202, 153, 24, 90]; + + 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[..], &expected[..]); + } +} diff --git a/src/rust/crypto/lib.rs b/src/rust/crypto/lib.rs new file mode 100644 index 0000000000..f72a859dd7 --- /dev/null +++ b/src/rust/crypto/lib.rs @@ -0,0 +1,45 @@ +// 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. +//! +//! ```rust,no_run +//! use crypto::digests::sha2::*; +//! +//! let mut hasher: Sha256 = Sha256::default(); +//! let mut result: [u8; 32] = [0u8; 32]; +//! +//! hasher.input(b"foo"); +//! hasher.input(b"bar"); +//! hasher.input(b"baz"); +//! +//! result.copy_from_slice(hasher.result().as_slice()); +//! +//! assert!(result == [b'X'; DIGEST256_LEN]); +//! ``` + +#[deny(missing_docs)] + +// External crates from cargo or TOR_RUST_DEPENDENCIES. +extern crate digest; +extern crate libc; +extern crate rand_core; + +// External dependencies for tests. +#[cfg(test)] +extern crate rand as rand_crate; + +// Our local crates. +extern crate external; +#[cfg(not(test))] +#[macro_use] +extern crate tor_log; + +pub mod digests; // Unfortunately named "digests" plural to avoid name conflict with the digest crate +pub mod rand; diff --git a/src/rust/crypto/rand/mod.rs b/src/rust/crypto/rand/mod.rs new file mode 100644 index 0000000000..82d02a70bb --- /dev/null +++ b/src/rust/crypto/rand/mod.rs @@ -0,0 +1,6 @@ +// Copyright (c) 2018, The Tor Project, Inc. +// Copyright (c) 2018, isis agora lovecruft +// See LICENSE for licensing information + +// Internal dependencies +pub mod rng; diff --git a/src/rust/crypto/rand/rng.rs b/src/rust/crypto/rand/rng.rs new file mode 100644 index 0000000000..07a0a7bdc7 --- /dev/null +++ b/src/rust/crypto/rand/rng.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2018, The Tor Project, Inc. +// Copyright (c) 2018, isis agora lovecruft +// See LICENSE for licensing information + +//! Wrappers for Tor's random number generators to provide implementations of +//! `rand_core` traits. + +// This is the real implementation, in use in production, which calls into our C +// wrappers in /src/common/crypto_rand.c, which call into OpenSSL, system +// libraries, and make syscalls. +#[cfg(not(test))] +mod internal { + use std::u64; + + use rand_core::CryptoRng; + use rand_core::Error; + use rand_core::RngCore; + use rand_core::impls::next_u32_via_fill; + use rand_core::impls::next_u64_via_fill; + + use external::c_tor_crypto_rand; + use external::c_tor_crypto_strongest_rand; + use external::c_tor_crypto_seed_rng; + + use tor_log::LogDomain; + use tor_log::LogSeverity; + + /// Largest strong entropy request permitted. + // + // C_RUST_COUPLED: `MAX_STRONGEST_RAND_SIZE` /src/common/crypto_rand.c + const MAX_STRONGEST_RAND_SIZE: usize = 256; + + /// A wrapper around OpenSSL's RNG. + pub struct TorRng { + // This private, zero-length field forces the struct to be treated the + // same as its opaque C couterpart. + _unused: [u8; 0], + } + + /// Mark `TorRng` as being suitable for cryptographic purposes. + impl CryptoRng for TorRng {} + + impl TorRng { + // C_RUST_COUPLED: `crypto_seed_rng()` /src/common/crypto_rand.c + #[allow(dead_code)] + pub fn new() -> Self { + if !c_tor_crypto_seed_rng() { + tor_log_msg!(LogSeverity::Warn, LogDomain::General, + "TorRng::from_seed()", + "The RNG could not be seeded!"); + } + // XXX also log success at info level —isis + TorRng{ _unused: [0u8; 0] } + } + } + + impl RngCore for TorRng { + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn next_u32(&mut self) -> u32 { + next_u32_via_fill(self) + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn next_u64(&mut self) -> u64 { + next_u64_via_fill(self) + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn fill_bytes(&mut self, dest: &mut [u8]) { + c_tor_crypto_rand(dest); + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } + } + + /// A CSPRNG which hashes together randomness from OpenSSL's RNG and entropy + /// obtained from the operating system. + pub struct TorStrongestRng { + // This private, zero-length field forces the struct to be treated the + // same as its opaque C couterpart. + _unused: [u8; 0], + } + + /// Mark `TorRng` as being suitable for cryptographic purposes. + impl CryptoRng for TorStrongestRng {} + + impl TorStrongestRng { + // C_RUST_COUPLED: `crypto_seed_rng()` /src/common/crypto_rand.c + #[allow(dead_code)] + pub fn new() -> Self { + if !c_tor_crypto_seed_rng() { + tor_log_msg!(LogSeverity::Warn, LogDomain::General, + "TorStrongestRng::from_seed()", + "The RNG could not be seeded!"); + } + // XXX also log success at info level —isis + TorStrongestRng{ _unused: [0u8; 0] } + } + } + + impl RngCore for TorStrongestRng { + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn next_u32(&mut self) -> u32 { + next_u32_via_fill(self) + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn next_u64(&mut self) -> u64 { + next_u64_via_fill(self) + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn fill_bytes(&mut self, dest: &mut [u8]) { + debug_assert!(dest.len() <= MAX_STRONGEST_RAND_SIZE); + + c_tor_crypto_strongest_rand(dest); + } + + // C_RUST_COUPLED: `crypto_strongest_rand()` /src/common/crypto_rand.c + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } + } +} + +// For testing, we expose a pure-Rust implementation. +#[cfg(test)] +mod internal { + // It doesn't matter if we pretend ChaCha is a CSPRNG in tests. + pub use rand_crate::ChaChaRng as TorRng; + pub use rand_crate::ChaChaRng as TorStrongestRng; +} + +// Finally, expose the public functionality of whichever appropriate internal +// module. +pub use self::internal::*; + |