Skip to main content

streamsha/
sha512.rs

1use crate::arith::{Word64, rotr};
2use crate::hash_state;
3use crate::hash_state::HashState;
4use crate::traits::*;
5use crate::consts::*;
6
7/// Calculates SHA-512
8pub struct Sha512 {
9    /// Hash values
10    h: [Word64; 8],
11    /// The max length of message (in bytes) defined in fips 180-4
12    message_len: u128,
13    /// The length of `current_block` in bytes
14    block_len: usize,
15    /// The incomplete block that is waiting to be filled and hashed
16    current_block: [u8; SHA512_BLOCK_SIZE],
17}
18
19impl Sha512 {
20    /// Create new instance
21    pub fn new() -> Self {
22        Self {
23            h: SHA512_H,
24            current_block: [0u8; SHA512_BLOCK_SIZE],
25            block_len: 0usize,
26            message_len: 0u128,
27        }
28    }
29    /// Compute hash for current block
30    fn process_block(&mut self) {
31        if self.block_len != SHA512_BLOCK_SIZE {
32            panic!("block is not filled");
33        }
34        let mut w = [Word64(0); 80];
35        for t in 0..16 {
36            w[t] = self.get_word64_in_block(t)
37        }
38        for t in 16..80 {
39            w[t] = Self::lsigma1(w[t - 2]) + w[t - 7] + Self::lsigma0(w[t - 15]) + w[t - 16];
40        }
41        let mut a = self.h[0];
42        let mut b = self.h[1];
43        let mut c = self.h[2];
44        let mut d = self.h[3];
45        let mut e = self.h[4];
46        let mut f = self.h[5];
47        let mut g = self.h[6];
48        let mut h = self.h[7];
49
50        for t in 0..80 {
51            let t1 = h + Self::sigma1(e) + Self::ch(e, f, g) + SHA512_K[t] + w[t];
52            let t2 = Self::sigma0(a) + Self::maj(a, b, c);
53            h = g;
54            g = f;
55            f = e;
56            e = d + t1;
57            d = c;
58            c = b;
59            b = a;
60            a = t1 + t2;
61        }
62        self.h[0] = a + self.h[0];
63        self.h[1] = b + self.h[1];
64        self.h[2] = c + self.h[2];
65        self.h[3] = d + self.h[3];
66        self.h[4] = e + self.h[4];
67        self.h[5] = f + self.h[5];
68        self.h[6] = g + self.h[6];
69        self.h[7] = h + self.h[7];
70
71        self.current_block = [0u8; SHA512_BLOCK_SIZE]; // next block
72        self.block_len = 0; // reset block
73    }
74
75    /// Conbines 8 byte and returns as Word64.
76    const fn get_word64_in_block(&self, i: usize) -> Word64 {
77        let m: u64 =
78              ((self.current_block[i * 8] as u64) << 56)
79            + ((self.current_block[i * 8 + 1] as u64) << 48)
80            + ((self.current_block[i * 8 + 2] as u64) << 40)
81            + ((self.current_block[i * 8 + 3] as u64) << 32)
82            + ((self.current_block[i * 8 + 4] as u64) << 24)
83            + ((self.current_block[i * 8 + 5] as u64) << 16)
84            + ((self.current_block[i * 8 + 6] as u64) << 8)
85            + (self.current_block[i * 8 + 7] as u64);
86        Word64(m)
87    }
88}
89
90/// SHA512 functions
91impl Sha512 {
92    fn sigma0(x: Word64) -> Word64 {
93        rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39)
94    }
95    fn sigma1(x: Word64) -> Word64 {
96        rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41)
97    }
98    fn lsigma0(x: Word64) -> Word64 {
99        rotr(x, 1) ^ rotr(x, 8) ^ (x >> 7)
100    }
101    fn lsigma1(x: Word64) -> Word64 {
102        rotr(x, 19) ^ rotr(x, 61) ^ (x >> 6)
103    }
104    fn ch(x: Word64, y: Word64, z: Word64) -> Word64 {
105        (x & y) ^ (!x & z)
106    }
107    fn maj(x: Word64, y: Word64, z: Word64) -> Word64 {
108        (x & y) ^ (x & z) ^ (y & z)
109    }
110}
111impl StreamHasher for Sha512 {
112    type Output = [u8; 64];
113    const BLOCK_SIZE: usize = SHA512_BLOCK_SIZE;
114    fn update(&mut self, buf: &[u8]) -> usize {
115        let len = buf.len();
116        if len == 0 {
117            // if no data or no remaining data, stop
118            return 0;
119        }
120        let writable_len = Self::BLOCK_SIZE - self.block_len;
121        let writable_area = &mut self.current_block[self.block_len..];
122
123        if len >= writable_len {
124            // overflows block or buf.len() == writable_len
125            writable_area.clone_from_slice(&buf[0..writable_len]); // fill block
126            self.block_len += writable_len;
127            self.message_len += writable_len as u128;
128            self.process_block(); // perform hash calculation
129            self.update(&buf[writable_len..]); // recursively write remaining
130        } else {
131            // don't fill block
132            let write_area = &mut self.current_block[self.block_len..self.block_len + len];
133            write_area.clone_from_slice(&buf[..]);
134            self.block_len += len;
135            self.message_len += len as u128;
136        }
137        len
138    }
139    fn finish(mut self) -> Self::Output {
140        self.current_block[self.block_len] = 0x80;
141        if self.block_len + 1 + 16 > Self::BLOCK_SIZE {
142            // data||0x80||size(u128) overflows block
143            
144            self.block_len = Self::BLOCK_SIZE;
145            self.process_block(); // perform hash calculation
146        }
147        let writable_area = &mut self.current_block[Self::BLOCK_SIZE - 16..Self::BLOCK_SIZE];
148        let len_bits = self.message_len * 8;
149        writable_area.clone_from_slice(&len_bits.to_be_bytes());
150        self.block_len = Self::BLOCK_SIZE;
151        self.process_block();
152        let mut final_hash: Self::Output = [0; 64];
153        for i in 0..8 {
154            let word_area = &mut final_hash[i * 8..i * 8 + 8];
155            word_area.clone_from_slice(&self.h[i].0.to_be_bytes());
156        }
157        return final_hash;
158    }
159    
160}
161impl Resumable for Sha512 {
162    fn pause(self) -> HashState {
163        let h: [u64; 8] = [
164            self.h[0].0,
165            self.h[1].0,
166            self.h[2].0,
167            self.h[3].0,
168            self.h[4].0,
169            self.h[5].0,
170            self.h[6].0,
171            self.h[7].0,
172        ];
173        HashState::Sha512(hash_state::Sha512HashState {
174            h,
175            message_len: self.message_len,
176            block_len: self.block_len,
177            current_block: self.current_block,
178        })
179    }
180    fn resume(hash_state: HashState) -> Result<Self, hash_state::Error> {
181        match hash_state {
182            HashState::Sha512(hs) => Ok(Self {
183                h: arr64![hs.h[0], hs.h[1], hs.h[2], hs.h[3], hs.h[4], hs.h[5], hs.h[6], hs.h[7]],
184                message_len: hs.message_len,
185                block_len: hs.block_len,
186                current_block: hs.current_block,
187            }),
188            _ => Err(hash_state::Error::HashTypeNotMatch),
189        }
190    }
191}
192impl Default for Sha512 {
193    fn default() -> Self {
194        Self::new()
195    }
196}