| // Copyright 2015-2017 Brian Smith. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| //! SHA-2 and the legacy SHA-1 digest algorithm. |
| //! |
| //! If all the data is available in a single contiguous slice then the `digest` |
| //! function should be used. Otherwise, the digest can be calculated in |
| //! multiple steps using `Context`. |
| |
| // Note on why are we doing things the hard way: It would be easy to implement |
| // this using the C `EVP_MD`/`EVP_MD_CTX` interface. However, if we were to do |
| // things that way, we'd have a hard dependency on `malloc` and other overhead. |
| // The goal for this implementation is to drive the overhead as close to zero |
| // as possible. |
| |
| use {c, init, polyfill}; |
| use core; |
| |
| // XXX: Replace with `const fn` when `const fn` is stable: |
| // https://github.com/rust-lang/rust/issues/24111 |
| #[cfg(target_endian = "little")] |
| macro_rules! u32x2 { |
| ( $first:expr, $second:expr ) => |
| ( ((($second as u64) << 32) | ($first as u64)) ) |
| } |
| |
| mod sha1; |
| |
| /// A context for multi-step (Init-Update-Finish) digest calculations. |
| /// |
| /// C analog: `EVP_MD_CTX`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use ring::digest; |
| /// |
| /// let one_shot = digest::digest(&digest::SHA384, b"hello, world"); |
| /// |
| /// let mut ctx = digest::Context::new(&digest::SHA384); |
| /// ctx.update(b"hello"); |
| /// ctx.update(b", "); |
| /// ctx.update(b"world"); |
| /// let multi_part = ctx.finish(); |
| /// |
| /// assert_eq!(&one_shot.as_ref(), &multi_part.as_ref()); |
| /// ``` |
| pub struct Context { |
| state: State, |
| |
| // Note that SHA-512 has a 128-bit input bit counter, but this |
| // implementation only supports up to 2^64-1 input bits for all algorithms, |
| // so a 64-bit counter is more than sufficient. |
| completed_data_blocks: u64, |
| |
| // TODO: More explicitly force 64-bit alignment for |pending|. |
| pending: [u8; MAX_BLOCK_LEN], |
| num_pending: usize, |
| |
| /// The context's algorithm. |
| pub algorithm: &'static Algorithm, |
| } |
| |
| impl Context { |
| /// Constructs a new context. |
| /// |
| /// C analogs: `EVP_DigestInit`, `EVP_DigestInit_ex` |
| pub fn new(algorithm: &'static Algorithm) -> Context { |
| init::init_once(); |
| |
| Context { |
| algorithm: algorithm, |
| state: algorithm.initial_state, |
| completed_data_blocks: 0, |
| pending: [0u8; MAX_BLOCK_LEN], |
| num_pending: 0, |
| } |
| } |
| |
| /// Updates the digest with all the data in `data`. `update` may be called |
| /// zero or more times until `finish` is called. It must not be called |
| /// after `finish` has been called. |
| /// |
| /// C analog: `EVP_DigestUpdate` |
| pub fn update(&mut self, data: &[u8]) { |
| if data.len() < self.algorithm.block_len - self.num_pending { |
| self.pending[self.num_pending..(self.num_pending + data.len())] |
| .copy_from_slice(data); |
| self.num_pending += data.len(); |
| return; |
| } |
| |
| let mut remaining = data; |
| if self.num_pending > 0 { |
| let to_copy = self.algorithm.block_len - self.num_pending; |
| self.pending[self.num_pending..self.algorithm.block_len] |
| .copy_from_slice(&data[..to_copy]); |
| |
| unsafe { |
| (self.algorithm.block_data_order)(&mut self.state, |
| self.pending.as_ptr(), 1); |
| } |
| self.completed_data_blocks = |
| self.completed_data_blocks.checked_add(1).unwrap(); |
| |
| remaining = &remaining[to_copy..]; |
| self.num_pending = 0; |
| } |
| |
| let num_blocks = remaining.len() / self.algorithm.block_len; |
| let num_to_save_for_later = remaining.len() % self.algorithm.block_len; |
| if num_blocks > 0 { |
| unsafe { |
| (self.algorithm.block_data_order)(&mut self.state, |
| remaining.as_ptr(), |
| num_blocks); |
| } |
| self.completed_data_blocks = |
| self.completed_data_blocks |
| .checked_add(polyfill::u64_from_usize(num_blocks)) |
| .unwrap(); |
| } |
| if num_to_save_for_later > 0 { |
| self.pending[..num_to_save_for_later] |
| .copy_from_slice(&remaining[(remaining.len() - |
| num_to_save_for_later)..]); |
| self.num_pending = num_to_save_for_later; |
| } |
| } |
| |
| /// Finalizes the digest calculation and returns the digest value. `finish` |
| /// consumes the context so it cannot be (mis-)used after `finish` has been |
| /// called. |
| /// |
| /// C analogs: `EVP_DigestFinal`, `EVP_DigestFinal_ex` |
| pub fn finish(mut self) -> Digest { |
| // We know |num_pending < self.algorithm.block_len|, because we would |
| // have processed the block otherwise. |
| |
| let mut padding_pos = self.num_pending; |
| self.pending[padding_pos] = 0x80; |
| padding_pos += 1; |
| |
| if padding_pos > self.algorithm.block_len - self.algorithm.len_len { |
| polyfill::slice::fill( |
| &mut self.pending[padding_pos..self.algorithm.block_len], 0); |
| unsafe { |
| (self.algorithm.block_data_order)(&mut self.state, |
| self.pending.as_ptr(), 1); |
| } |
| // We don't increase |self.completed_data_blocks| because the |
| // padding isn't data, and so it isn't included in the data length. |
| padding_pos = 0; |
| } |
| |
| polyfill::slice::fill( |
| &mut self.pending[padding_pos..(self.algorithm.block_len - 8)], 0); |
| |
| // Output the length, in bits, in big endian order. |
| let mut completed_data_bits: u64 = self.completed_data_blocks |
| .checked_mul(polyfill::u64_from_usize(self.algorithm.block_len)) |
| .unwrap() |
| .checked_add(polyfill::u64_from_usize(self.num_pending)).unwrap() |
| .checked_mul(8).unwrap(); |
| |
| for b in (&mut self.pending[(self.algorithm.block_len - 8).. |
| self.algorithm.block_len]).into_iter().rev() { |
| *b = completed_data_bits as u8; |
| completed_data_bits /= 0x100; |
| } |
| unsafe { |
| (self.algorithm.block_data_order)(&mut self.state, |
| self.pending.as_ptr(), 1); |
| } |
| |
| Digest { |
| algorithm: self.algorithm, |
| value: (self.algorithm.format_output)(&self.state), |
| } |
| } |
| |
| /// The algorithm that this context is using. |
| #[inline(always)] |
| pub fn algorithm(&self) -> &'static Algorithm { self.algorithm } |
| } |
| |
| // XXX: This should just be `#[derive(Clone)]` but that doesn't work because |
| // `[u8; 128]` doesn't implement `Clone`. |
| impl Clone for Context { |
| fn clone(&self) -> Context { |
| Context { |
| state: self.state, |
| pending: self.pending, |
| completed_data_blocks: self.completed_data_blocks, |
| num_pending: self.num_pending, |
| algorithm: self.algorithm, |
| } |
| } |
| } |
| |
| /// Returns the digest of `data` using the given digest algorithm. |
| /// |
| /// C analog: `EVP_Digest` |
| /// |
| /// # Examples: |
| /// |
| /// ``` |
| /// # #[cfg(feature = "use_heap")] |
| /// # fn main() { |
| /// use ring::{digest, test}; |
| /// |
| /// let expected_hex = |
| /// "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b"; |
| /// let expected: Vec<u8> = test::from_hex(expected_hex).unwrap(); |
| /// let actual = digest::digest(&digest::SHA256, b"hello, world"); |
| /// |
| /// assert_eq!(&expected, &actual.as_ref()); |
| /// # } |
| /// |
| /// # #[cfg(not(feature = "use_heap"))] |
| /// # fn main() { } |
| /// ``` |
| pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest { |
| let mut ctx = Context::new(algorithm); |
| ctx.update(data); |
| ctx.finish() |
| } |
| |
| /// A calculated digest value. |
| /// |
| /// Use `as_ref` to get the value as a `&[u8]`. |
| #[derive(Clone, Copy)] |
| pub struct Digest { |
| value: Output, |
| algorithm: &'static Algorithm, |
| } |
| |
| impl Digest { |
| /// The algorithm that was used to calculate the digest value. |
| #[inline(always)] |
| pub fn algorithm(&self) -> &'static Algorithm { self.algorithm } |
| } |
| |
| impl AsRef<[u8]> for Digest { |
| #[inline(always)] |
| fn as_ref(&self) -> &[u8] { |
| &(polyfill::slice::u64_as_u8(&self.value))[..self.algorithm.output_len] |
| } |
| } |
| |
| impl core::fmt::Debug for Digest { |
| fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { |
| write!(fmt, "{:?}:", self.algorithm)?; |
| for byte in self.as_ref() { |
| write!(fmt, "{:02x}", byte)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| /// A digest algorithm. |
| /// |
| /// C analog: `EVP_MD` |
| pub struct Algorithm { |
| /// C analog: `EVP_MD_size` |
| pub output_len: usize, |
| |
| /// The size of the chaining value of the digest function, in bytes. For |
| /// non-truncated algorithms (SHA-1, SHA-256, SHA-512), this is equal to |
| /// `output_len`. For truncated algorithms (e.g. SHA-384, SHA-512/256), |
| /// this is equal to the length before truncation. This is mostly helpful |
| /// for determining the size of an HMAC key that is appropriate for the |
| /// digest algorithm. |
| pub chaining_len: usize, |
| |
| /// C analog: `EVP_MD_block_size` |
| pub block_len: usize, |
| |
| /// The length of the length in the padding. |
| len_len: usize, |
| |
| block_data_order: unsafe extern fn(state: &mut State, data: *const u8, |
| num: c::size_t), |
| format_output: fn(input: &State) -> Output, |
| |
| initial_state: State, |
| |
| id: AlgorithmID, |
| } |
| |
| #[derive(Eq, PartialEq)] |
| #[allow(non_camel_case_types)] |
| enum AlgorithmID { |
| SHA1, |
| SHA256, |
| SHA384, |
| SHA512, |
| SHA512_256, |
| } |
| |
| impl PartialEq for Algorithm { |
| fn eq(&self, other: &Self) -> bool { self.id == other.id } |
| } |
| |
| impl Eq for Algorithm {} |
| |
| impl core::fmt::Debug for Algorithm { |
| fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { |
| // This would have to change if/when we add other algorithms with the |
| // same lengths. |
| let (n, suffix) = |
| if self.output_len == SHA512_256_OUTPUT_LEN && |
| self.block_len == SHA512_BLOCK_LEN { |
| (512, "_256") |
| } else if self.output_len == 20 { |
| (1, "") |
| } else { |
| (self.output_len * 8, "") |
| }; |
| write!(fmt, "SHA{}{}", n, suffix) |
| } |
| } |
| |
| /// SHA-1 as specified in [FIPS 180-4]. Deprecated. |
| /// |
| /// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf |
| pub static SHA1: Algorithm = Algorithm { |
| output_len: sha1::OUTPUT_LEN, |
| chaining_len: sha1::CHAINING_LEN, |
| block_len: sha1::BLOCK_LEN, |
| len_len: 64 / 8, |
| block_data_order: sha1::block_data_order, |
| format_output: sha256_format_output, |
| initial_state: [ |
| u32x2!(0x67452301u32, 0xefcdab89u32), |
| u32x2!(0x98badcfeu32, 0x10325476u32), |
| u32x2!(0xc3d2e1f0u32, 0u32), |
| 0, 0, 0, 0, 0, |
| ], |
| id: AlgorithmID::SHA1, |
| }; |
| |
| /// SHA-256 as specified in [FIPS 180-4]. |
| /// |
| /// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf |
| pub static SHA256: Algorithm = Algorithm { |
| output_len: SHA256_OUTPUT_LEN, |
| chaining_len: SHA256_OUTPUT_LEN, |
| block_len: 512 / 8, |
| len_len: 64 / 8, |
| block_data_order: GFp_sha256_block_data_order, |
| format_output: sha256_format_output, |
| initial_state: [ |
| u32x2!(0x6a09e667u32, 0xbb67ae85u32), |
| u32x2!(0x3c6ef372u32, 0xa54ff53au32), |
| u32x2!(0x510e527fu32, 0x9b05688cu32), |
| u32x2!(0x1f83d9abu32, 0x5be0cd19u32), |
| 0, 0, 0, 0, |
| ], |
| id: AlgorithmID::SHA256, |
| }; |
| |
| /// SHA-384 as specified in [FIPS 180-4]. |
| /// |
| /// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf |
| pub static SHA384: Algorithm = Algorithm { |
| output_len: SHA384_OUTPUT_LEN, |
| chaining_len: SHA512_OUTPUT_LEN, |
| block_len: SHA512_BLOCK_LEN, |
| len_len: SHA512_LEN_LEN, |
| block_data_order: GFp_sha512_block_data_order, |
| format_output: sha512_format_output, |
| initial_state: [ |
| 0xcbbb9d5dc1059ed8, |
| 0x629a292a367cd507, |
| 0x9159015a3070dd17, |
| 0x152fecd8f70e5939, |
| 0x67332667ffc00b31, |
| 0x8eb44a8768581511, |
| 0xdb0c2e0d64f98fa7, |
| 0x47b5481dbefa4fa4, |
| ], |
| id: AlgorithmID::SHA384, |
| }; |
| |
| /// SHA-512 as specified in [FIPS 180-4]. |
| /// |
| /// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf |
| pub static SHA512: Algorithm = Algorithm { |
| output_len: SHA512_OUTPUT_LEN, |
| chaining_len: SHA512_OUTPUT_LEN, |
| block_len: SHA512_BLOCK_LEN, |
| len_len: SHA512_LEN_LEN, |
| block_data_order: GFp_sha512_block_data_order, |
| format_output: sha512_format_output, |
| initial_state: [ |
| 0x6a09e667f3bcc908, |
| 0xbb67ae8584caa73b, |
| 0x3c6ef372fe94f82b, |
| 0xa54ff53a5f1d36f1, |
| 0x510e527fade682d1, |
| 0x9b05688c2b3e6c1f, |
| 0x1f83d9abfb41bd6b, |
| 0x5be0cd19137e2179, |
| ], |
| id: AlgorithmID::SHA512, |
| }; |
| |
| /// SHA-512/256 as specified in [FIPS 180-4]. |
| /// |
| /// This is *not* the same as just truncating the output of SHA-512, as |
| /// SHA-512/256 has its own initial state distinct from SHA-512's initial |
| /// state. |
| /// |
| /// [FIPS 180-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf |
| pub static SHA512_256: Algorithm = Algorithm { |
| output_len: SHA512_256_OUTPUT_LEN, |
| chaining_len: SHA512_OUTPUT_LEN, |
| block_len: SHA512_BLOCK_LEN, |
| len_len: SHA512_LEN_LEN, |
| block_data_order: GFp_sha512_block_data_order, |
| format_output: sha512_format_output, |
| initial_state: [ |
| 0x22312194fc2bf72c, |
| 0x9f555fa3c84c64c2, |
| 0x2393b86b6f53b151, |
| 0x963877195940eabd, |
| 0x96283ee2a88effe3, |
| 0xbe5e1e2553863992, |
| 0x2b0199fc2c85b8aa, |
| 0x0eb72ddc81c52ca2, |
| ], |
| id: AlgorithmID::SHA512_256, |
| }; |
| |
| // We use u64 to try to ensure 64-bit alignment/padding. |
| type State = [u64; MAX_CHAINING_LEN / 8]; |
| |
| type Output = [u64; MAX_OUTPUT_LEN / 8]; |
| |
| /// The maximum block length (`Algorithm::block_len`) of all the algorithms in |
| /// this module. |
| pub const MAX_BLOCK_LEN: usize = 1024 / 8; |
| |
| /// The maximum output length (`Algorithm::output_len`) of all the algorithms |
| /// in this module. |
| pub const MAX_OUTPUT_LEN: usize = 512 / 8; |
| |
| /// The maximum chaining length (`Algorithm::chaining_len`) of all the |
| /// algorithms in this module. |
| pub const MAX_CHAINING_LEN: usize = MAX_OUTPUT_LEN; |
| |
| fn sha256_format_output(input: &State) -> Output { |
| let input = &polyfill::slice::u64_as_u32(input)[..8]; |
| [u32x2!(input[0].to_be(), input[1].to_be()), |
| u32x2!(input[2].to_be(), input[3].to_be()), |
| u32x2!(input[4].to_be(), input[5].to_be()), |
| u32x2!(input[6].to_be(), input[7].to_be()), |
| 0, |
| 0, |
| 0, |
| 0] |
| } |
| |
| fn sha512_format_output(input: &State) -> Output { |
| [input[0].to_be(), |
| input[1].to_be(), |
| input[2].to_be(), |
| input[3].to_be(), |
| input[4].to_be(), |
| input[5].to_be(), |
| input[6].to_be(), |
| input[7].to_be()] |
| } |
| |
| /// The length of the output of SHA-1, in bytes. |
| pub const SHA1_OUTPUT_LEN: usize = sha1::OUTPUT_LEN; |
| |
| /// The length of the output of SHA-256, in bytes. |
| pub const SHA256_OUTPUT_LEN: usize = 256 / 8; |
| |
| /// The length of the output of SHA-384, in bytes. |
| pub const SHA384_OUTPUT_LEN: usize = 384 / 8; |
| |
| /// The length of the output of SHA-512, in bytes. |
| pub const SHA512_OUTPUT_LEN: usize = 512 / 8; |
| |
| /// The length of the output of SHA-512/256, in bytes. |
| pub const SHA512_256_OUTPUT_LEN: usize = 256 / 8; |
| |
| /// The length of a block for SHA-512-based algorithms, in bytes. |
| const SHA512_BLOCK_LEN: usize = 1024 / 8; |
| |
| /// The length of the length field for SHA-512-based algorithms, in bytes. |
| const SHA512_LEN_LEN: usize = 128 / 8; |
| |
| extern { |
| fn GFp_sha256_block_data_order(state: &mut State, data: *const u8, |
| num: c::size_t); |
| fn GFp_sha512_block_data_order(state: &mut State, data: *const u8, |
| num: c::size_t); |
| } |
| |
| |
| #[cfg(test)] |
| pub mod test_util { |
| use super::super::digest; |
| |
| pub static ALL_ALGORITHMS: [&'static digest::Algorithm; 5] = [ |
| &digest::SHA1, |
| &digest::SHA256, |
| &digest::SHA384, |
| &digest::SHA512, |
| &digest::SHA512_256, |
| ]; |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| |
| mod max_input { |
| use super::super::super::digest; |
| |
| macro_rules! max_input_tests { |
| ( $algorithm_name:ident ) => { |
| #[allow(non_snake_case)] |
| mod $algorithm_name { |
| use super::super::super::super::digest; |
| |
| #[test] |
| fn max_input_test() { |
| super::max_input_test(&digest::$algorithm_name); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn too_long_input_test_block() { |
| super::too_long_input_test_block( |
| &digest::$algorithm_name); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn too_long_input_test_byte() { |
| super::too_long_input_test_byte( |
| &digest::$algorithm_name); |
| } |
| } |
| } |
| } |
| |
| fn max_input_test(alg: &'static digest::Algorithm) { |
| let mut context = nearly_full_context(alg); |
| let next_input = vec![0u8; alg.block_len - 1]; |
| context.update(&next_input); |
| let _ = context.finish(); // no panic |
| } |
| |
| fn too_long_input_test_block(alg: &'static digest::Algorithm) { |
| let mut context = nearly_full_context(alg); |
| let next_input = vec![0u8; alg.block_len]; |
| context.update(&next_input); |
| let _ = context.finish(); // should panic |
| } |
| |
| fn too_long_input_test_byte(alg: &'static digest::Algorithm) { |
| let mut context = nearly_full_context(alg); |
| let next_input = vec![0u8; alg.block_len - 1]; |
| context.update(&next_input); // no panic |
| context.update(&[0]); |
| let _ = context.finish(); // should panic |
| } |
| |
| fn nearly_full_context(alg: &'static digest::Algorithm) |
| -> digest::Context { |
| // All implementations currently support up to 2^64-1 bits |
| // of input; according to the spec, SHA-384 and SHA-512 |
| // support up to 2^128-1, but that's not implemented yet. |
| let max_bytes = 1u64 << (64 - 3); |
| let max_blocks = max_bytes / (alg.block_len as u64); |
| digest::Context { |
| algorithm: alg, |
| state: alg.initial_state, |
| completed_data_blocks: max_blocks - 1, |
| pending: [0u8; digest::MAX_BLOCK_LEN], |
| num_pending: 0, |
| } |
| } |
| |
| max_input_tests!(SHA1); |
| max_input_tests!(SHA256); |
| max_input_tests!(SHA384); |
| max_input_tests!(SHA512); |
| } |
| } |