// 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 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) -> 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::(), 64usize, concat!("Size of: ", stringify!(common_digests_t))); assert_eq!(::std::mem::align_of::(), 1usize, concat!("Alignment of ", stringify!(common_digests_t))); } #[test] fn test_layout_crypto_digest_t() { assert_eq!(::std::mem::size_of::(), 0usize, concat!("Size of: ", stringify!(crypto_digest_t))); assert_eq!(::std::mem::align_of::(), 1usize, concat!("Alignment of ", stringify!(crypto_digest_t))); } }