1use 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
10pub fn sha_512(m:Vec<U1024>) -> U512 {
12 sha512_internal::<Sha512>(m)
13}
14
15pub 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
27pub 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
64fn 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
95fn 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
116fn 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
125fn sha512_message_schedule(m:U1024) -> [u64;80] {
128 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}