jisp_sha2/
sha512.rs

1//! This is the SHA-512 Algorithm. 
2//! You can use the standard implementations of [sha_512] and [sha_384] or you can use custom constants and initial hash values with [sha512_internal]
3
4use std::ops::{BitXor, BitAnd};
5
6use crypto_bigint::{U384, U512,U1024};
7use crate::conversions::{self, to_u64_words};
8use crate::constants::{Constants, Sha512, Sha384};
9
10/// The SHA-512 Algorithm, only use this after having [preprocessed](crate::preprocessing::sha512_preprocessing) your data into message blocks
11pub fn sha_512(m:Vec<U1024>) -> U512 {
12    sha512_internal::<Sha512>(m)
13}
14
15/// The SHA-384 Algorithm, only use this after having [preprocessed](crate::preprocessing::sha512_preprocessing) your data into message blocks
16/// Note that this is essentially the [SHA-512](sha_512) algorithm, except with different constants and a truncated result.
17pub fn sha_384(m:Vec<U1024>) -> U384 {
18    let res = sha512_internal::<Sha384>(m);
19    let words = to_u64_words(res);
20    let mut truncated_words = [0u64; 6];
21    for i in 0..6 {
22        truncated_words[i] = words[i];
23    } 
24    return conversions::from_u64_words(&mut truncated_words);
25}
26
27/// The internal loop of the SHA-512 algorithm, 
28/// you can use different initial hash and constant values by implementing the [Constants](crate::constants::Constants) trait on a new object
29pub fn sha512_internal<C:Constants<80,u64>>(msg:Vec<U1024>) -> U512 {
30    let mut hash = C::initial_hash();
31    for block in msg {
32        let registers = sha512_compression::<C>(&hash, block);
33        for i in 0..8 {
34            hash[i] = hash[i].wrapping_add(registers[i]);
35        }
36    }
37    conversions::from_u64_words(&mut hash)
38}
39
40fn sha512_compression<C:Constants<80,u64>>(intermediate_hash:&[u64;8], msg:U1024) -> [u64;8] {
41    let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = intermediate_hash.clone();
42    let constants_k = C::constant_words();
43    let expanded_blocks = sha512_message_schedule(msg);
44
45    for j in 0..80 {
46        let T1 = wrap_sum(vec![
47            h, sigma_l1(e), ch(e,f,g), constants_k[j], expanded_blocks[j] 
48        ]);
49        let T2 = sigma_l0(a).wrapping_add(maj(a,b,c));
50
51        h = g;
52        g = f;
53        f = e;
54        e = d.wrapping_add(T1);
55        d = c;
56        c = b;
57        b = a;
58        a = T1.wrapping_add(T2);
59    }
60
61    [a, b, c, d, e, f, g, h]
62}
63
64// ------ Six Logical Functions -------
65
66fn ch(x:u64, y:u64, z:u64) -> u64 {
67    let x_and_y = x.bitand(y);
68    let x_and_z = inv(x).bitand(z);
69    return x_and_y.bitxor(x_and_z);
70}
71
72fn maj(x:u64, y:u64, z:u64) -> u64 {
73    let x_and_y = x.bitand(y);
74    let y_and_z = y.bitand(z);
75    let x_and_z = x.bitand(z);
76    return xor_sum(vec![x_and_y, y_and_z, x_and_z]);
77}
78
79fn sigma_l0(x:u64) -> u64 {
80    return xor_sum(vec![s(x,28), s(x,34), s(x,39)]);
81}
82
83fn sigma_l1(x:u64) -> u64 {
84    return xor_sum(vec![s(x,14), s(x,18), s(x,41)]);
85}
86
87fn sigma_s0(x:u64) -> u64 {
88    return xor_sum(vec![s(x,1), s(x,8), r(x,7)]);
89}
90
91fn sigma_s1(x:u64) -> u64 {
92    return xor_sum(vec![s(x,19), s(x, 61), r(x,6)])
93}
94
95// --------- addendum -------------
96fn inv(x:u64) -> u64 {
97    x.bitxor(u64::MAX)
98}
99
100fn xor_sum(v:Vec<u64>) -> u64 {
101    let mut res = 0u64;
102    for u in v {
103        res = res.bitxor(u);
104    }
105    return res;
106}
107
108fn wrap_sum(v:Vec<u64>) -> u64 {
109    let mut res = 0u64;
110    for u in v {
111        res = res.wrapping_add(u);
112    }
113    return res;
114}
115
116//Renamed functions to fit with the SHA-2 design document
117fn s(uint:u64, rot:u8) -> u64 {
118    uint.rotate_right(rot as u32)
119} 
120fn r(uint:u64, shift:u8) -> u64 {
121    uint >> shift
122}
123
124
125// ------------------ Message Schedule ----------------
126
127fn sha512_message_schedule(m:U1024) -> [u64;80] {
128    //word vector of length 16
129    let m = conversions::to_u64_words(m); 
130    let mut w = [0u64; 80];
131    for i in 0..16 {
132        w[i] = m[i];
133    }
134
135    for i in 16..80 {
136        w[i] = wrap_sum(vec![
137            sigma_s1(w[i - 2]),
138            w[i - 7],
139            sigma_s0(w[i - 15]),
140            w[i - 16]
141        ]);
142    }
143
144    return w;
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    #[test]
151    fn simple_sigma_l0() {
152        let y = sigma_l0(1);
153        let expected = (1 << 36) + (1 << 30) + (1 << 25);
154        assert_eq!(y, expected);
155    }
156
157    #[test]
158    fn simple_sigma_s0() {
159        let y = sigma_s0(1 << 10);
160        let expected = (1 << 9) + (1 << 2) + (1 << 3);
161        assert_eq!(y,expected);
162    }
163}