Skip to content

Commit d656e7c

Browse files
authored
sha2: simplify the soft backend (#689)
This change makes it easier to read the software backend code and makes it similar to the `soft-compact` backend. It also slightly improves codegen (e.g. on x86-64 for SHA-256 it results in [~3330][0] vs [~3490][1] instructions). [0]: https://rust.godbolt.org/z/xsees1r7r [1]: https://rust.godbolt.org/z/5q19WYW14
1 parent 9b497dd commit d656e7c

2 files changed

Lines changed: 146 additions & 401 deletions

File tree

sha2/src/sha256/soft.rs

Lines changed: 76 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,221 +1,87 @@
1-
#![allow(clippy::many_single_char_names)]
21
use crate::consts::K32;
32

4-
#[inline(always)]
5-
fn shr(v: [u32; 4], o: u32) -> [u32; 4] {
6-
[v[0] >> o, v[1] >> o, v[2] >> o, v[3] >> o]
7-
}
8-
9-
#[inline(always)]
10-
fn shl(v: [u32; 4], o: u32) -> [u32; 4] {
11-
[v[0] << o, v[1] << o, v[2] << o, v[3] << o]
12-
}
13-
14-
#[inline(always)]
15-
fn or(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
16-
[a[0] | b[0], a[1] | b[1], a[2] | b[2], a[3] | b[3]]
17-
}
18-
19-
#[inline(always)]
20-
fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
21-
[a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]
22-
}
23-
24-
#[inline(always)]
25-
fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
26-
[
27-
a[0].wrapping_add(b[0]),
28-
a[1].wrapping_add(b[1]),
29-
a[2].wrapping_add(b[2]),
30-
a[3].wrapping_add(b[3]),
31-
]
32-
}
33-
34-
#[inline(always)]
35-
fn add_round_const(mut a: [u32; 4], i: usize) -> [u32; 4] {
36-
fn k(i: usize, j: usize) -> u32 {
37-
// `read_volatile` forces compiler to read round constants from the static
38-
// instead of inlining them, which improves codegen and performance on some platforms.
39-
// On x86 targets 32-bit constants can be encoded using immediate argument on the `add`
40-
// instruction, so it's more efficient to inline them.
41-
cfg_if::cfg_if! {
42-
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
43-
use core::ptr::read as r;
44-
} else {
45-
use core::ptr::read_volatile as r;
46-
}
3+
#[rustfmt::skip]
4+
macro_rules! repeat64 {
5+
($i:ident, $b:block) => {
6+
let $i = 0; $b; let $i = 1; $b; let $i = 2; $b; let $i = 3; $b;
7+
let $i = 4; $b; let $i = 5; $b; let $i = 6; $b; let $i = 7; $b;
8+
let $i = 8; $b; let $i = 9; $b; let $i = 10; $b; let $i = 11; $b;
9+
let $i = 12; $b; let $i = 13; $b; let $i = 14; $b; let $i = 15; $b;
10+
let $i = 16; $b; let $i = 17; $b; let $i = 18; $b; let $i = 19; $b;
11+
let $i = 20; $b; let $i = 21; $b; let $i = 22; $b; let $i = 23; $b;
12+
let $i = 24; $b; let $i = 25; $b; let $i = 26; $b; let $i = 27; $b;
13+
let $i = 28; $b; let $i = 29; $b; let $i = 30; $b; let $i = 31; $b;
14+
let $i = 32; $b; let $i = 33; $b; let $i = 34; $b; let $i = 35; $b;
15+
let $i = 36; $b; let $i = 37; $b; let $i = 38; $b; let $i = 39; $b;
16+
let $i = 40; $b; let $i = 41; $b; let $i = 42; $b; let $i = 43; $b;
17+
let $i = 44; $b; let $i = 45; $b; let $i = 46; $b; let $i = 47; $b;
18+
let $i = 48; $b; let $i = 49; $b; let $i = 50; $b; let $i = 51; $b;
19+
let $i = 52; $b; let $i = 53; $b; let $i = 54; $b; let $i = 55; $b;
20+
let $i = 56; $b; let $i = 57; $b; let $i = 58; $b; let $i = 59; $b;
21+
let $i = 60; $b; let $i = 61; $b; let $i = 62; $b; let $i = 63; $b;
22+
};
23+
}
24+
25+
/// Read round constant
26+
fn rk(i: usize) -> u32 {
27+
// `read_volatile` forces compiler to read round constants from the static
28+
// instead of inlining them, which improves codegen and performance on some platforms.
29+
// On x86 targets 32-bit constants can be encoded using immediate argument on the `add`
30+
// instruction, so it's more efficient to inline them.
31+
cfg_if::cfg_if! {
32+
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
33+
use core::ptr::read as r;
34+
} else {
35+
use core::ptr::read_volatile as r;
4736
}
48-
49-
unsafe { r(K32.as_ptr().add(4 * i + j)) }
50-
}
51-
52-
a[3] = a[3].wrapping_add(k(i, 0));
53-
a[2] = a[2].wrapping_add(k(i, 1));
54-
a[1] = a[1].wrapping_add(k(i, 2));
55-
a[0] = a[0].wrapping_add(k(i, 3));
56-
a
57-
}
58-
59-
fn sha256load(v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
60-
[v3[3], v2[0], v2[1], v2[2]]
61-
}
62-
63-
fn sha256swap(v0: [u32; 4]) -> [u32; 4] {
64-
[v0[2], v0[3], v0[0], v0[1]]
65-
}
66-
67-
fn sha256msg1(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] {
68-
// sigma 0 on vectors
69-
#[inline]
70-
fn sigma0x4(x: [u32; 4]) -> [u32; 4] {
71-
let t1 = or(shr(x, 7), shl(x, 25));
72-
let t2 = or(shr(x, 18), shl(x, 14));
73-
let t3 = shr(x, 3);
74-
xor(xor(t1, t2), t3)
75-
}
76-
77-
add(v0, sigma0x4(sha256load(v0, v1)))
78-
}
79-
80-
fn sha256msg2(v4: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
81-
macro_rules! sigma1 {
82-
($a:expr) => {
83-
$a.rotate_right(17) ^ $a.rotate_right(19) ^ ($a >> 10)
84-
};
8537
}
8638

87-
let [x3, x2, x1, x0] = v4;
88-
let [w15, w14, _, _] = v3;
89-
90-
let w16 = x0.wrapping_add(sigma1!(w14));
91-
let w17 = x1.wrapping_add(sigma1!(w15));
92-
let w18 = x2.wrapping_add(sigma1!(w16));
93-
let w19 = x3.wrapping_add(sigma1!(w17));
94-
95-
[w19, w18, w17, w16]
96-
}
97-
98-
fn sha256_digest_round_x2(cdgh: [u32; 4], abef: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
99-
macro_rules! big_sigma0 {
100-
($a:expr) => {
101-
($a.rotate_right(2) ^ $a.rotate_right(13) ^ $a.rotate_right(22))
102-
};
39+
unsafe {
40+
let p = K32.as_ptr().add(i);
41+
r(p)
10342
}
104-
macro_rules! big_sigma1 {
105-
($a:expr) => {
106-
($a.rotate_right(6) ^ $a.rotate_right(11) ^ $a.rotate_right(25))
107-
};
108-
}
109-
macro_rules! bool3ary_202 {
110-
($a:expr, $b:expr, $c:expr) => {
111-
$c ^ ($a & ($b ^ $c))
112-
};
113-
} // Choose, MD5F, SHA1C
114-
macro_rules! bool3ary_232 {
115-
($a:expr, $b:expr, $c:expr) => {
116-
($a & $b) ^ ($a & $c) ^ ($b & $c)
117-
};
118-
} // Majority, SHA1M
119-
120-
let [_, _, wk1, wk0] = wk;
121-
let [a0, b0, e0, f0] = abef;
122-
let [c0, d0, g0, h0] = cdgh;
123-
124-
// a round
125-
let x0 = big_sigma1!(e0)
126-
.wrapping_add(bool3ary_202!(e0, f0, g0))
127-
.wrapping_add(wk0)
128-
.wrapping_add(h0);
129-
let y0 = big_sigma0!(a0).wrapping_add(bool3ary_232!(a0, b0, c0));
130-
let (a1, b1, c1, d1, e1, f1, g1, h1) = (
131-
x0.wrapping_add(y0),
132-
a0,
133-
b0,
134-
c0,
135-
x0.wrapping_add(d0),
136-
e0,
137-
f0,
138-
g0,
139-
);
140-
141-
// a round
142-
let x1 = big_sigma1!(e1)
143-
.wrapping_add(bool3ary_202!(e1, f1, g1))
144-
.wrapping_add(wk1)
145-
.wrapping_add(h1);
146-
let y1 = big_sigma0!(a1).wrapping_add(bool3ary_232!(a1, b1, c1));
147-
let (a2, b2, _, _, e2, f2, _, _) = (
148-
x1.wrapping_add(y1),
149-
a1,
150-
b1,
151-
c1,
152-
x1.wrapping_add(d1),
153-
e1,
154-
f1,
155-
g1,
156-
);
157-
158-
[a2, b2, e2, f2]
159-
}
160-
161-
fn schedule(v0: [u32; 4], v1: [u32; 4], v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
162-
let t1 = sha256msg1(v0, v1);
163-
let t2 = sha256load(v2, v3);
164-
let t3 = add(t1, t2);
165-
sha256msg2(t3, v3)
166-
}
167-
168-
macro_rules! rounds4 {
169-
($abef:ident, $cdgh:ident, $rest:expr, $i:expr) => {{
170-
let t1 = add_round_const($rest, $i);
171-
$cdgh = sha256_digest_round_x2($cdgh, $abef, t1);
172-
let t2 = sha256swap(t1);
173-
$abef = sha256_digest_round_x2($abef, $cdgh, t2);
174-
}};
175-
}
176-
177-
macro_rules! schedule_rounds4 {
178-
(
179-
$abef:ident, $cdgh:ident,
180-
$w0:expr, $w1:expr, $w2:expr, $w3:expr, $w4:expr,
181-
$i: expr
182-
) => {{
183-
$w4 = schedule($w0, $w1, $w2, $w3);
184-
rounds4!($abef, $cdgh, $w4, $i);
185-
}};
18643
}
18744

18845
/// Process a block with the SHA-256 algorithm.
189-
fn sha256_digest_block_u32(state: &mut [u32; 8], block: [u32; 16]) {
190-
let mut abef = [state[0], state[1], state[4], state[5]];
191-
let mut cdgh = [state[2], state[3], state[6], state[7]];
192-
193-
// Rounds 0..64
194-
let mut w0 = [block[3], block[2], block[1], block[0]];
195-
let mut w1 = [block[7], block[6], block[5], block[4]];
196-
let mut w2 = [block[11], block[10], block[9], block[8]];
197-
let mut w3 = [block[15], block[14], block[13], block[12]];
198-
let mut w4;
199-
200-
rounds4!(abef, cdgh, w0, 0);
201-
rounds4!(abef, cdgh, w1, 1);
202-
rounds4!(abef, cdgh, w2, 2);
203-
rounds4!(abef, cdgh, w3, 3);
204-
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 4);
205-
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 5);
206-
schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 6);
207-
schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 7);
208-
schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 8);
209-
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 9);
210-
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 10);
211-
schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 11);
212-
schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 12);
213-
schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 13);
214-
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 14);
215-
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 15);
46+
fn compress_block(state: &mut [u32; 8], block: &[u8; 64]) {
47+
let mut block = super::to_u32s(block);
48+
let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = *state;
49+
50+
repeat64!(i, {
51+
let w = if i < 16 {
52+
block[i]
53+
} else {
54+
let w15 = block[(i - 15) % 16];
55+
let s0 = (w15.rotate_right(7)) ^ (w15.rotate_right(18)) ^ (w15 >> 3);
56+
let w2 = block[(i - 2) % 16];
57+
let s1 = (w2.rotate_right(17)) ^ (w2.rotate_right(19)) ^ (w2 >> 10);
58+
block[i % 16] = block[i % 16]
59+
.wrapping_add(s0)
60+
.wrapping_add(block[(i - 7) % 16])
61+
.wrapping_add(s1);
62+
block[i % 16]
63+
};
21664

217-
let [a, b, e, f] = abef;
218-
let [c, d, g, h] = cdgh;
65+
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
66+
let ch = (e & f) ^ ((!e) & g);
67+
let t1 = s1
68+
.wrapping_add(ch)
69+
.wrapping_add(rk(i))
70+
.wrapping_add(w)
71+
.wrapping_add(h);
72+
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
73+
let maj = (a & b) ^ (a & c) ^ (b & c);
74+
let t2 = s0.wrapping_add(maj);
75+
76+
h = g;
77+
g = f;
78+
f = e;
79+
e = d.wrapping_add(t1);
80+
d = c;
81+
c = b;
82+
b = a;
83+
a = t1.wrapping_add(t2);
84+
});
21985

22086
state[0] = state[0].wrapping_add(a);
22187
state[1] = state[1].wrapping_add(b);
@@ -228,7 +94,7 @@ fn sha256_digest_block_u32(state: &mut [u32; 8], block: [u32; 16]) {
22894
}
22995

23096
pub fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
231-
for block in blocks.iter().map(super::to_u32s) {
232-
sha256_digest_block_u32(state, block);
97+
for block in blocks {
98+
compress_block(state, block);
23399
}
234100
}

0 commit comments

Comments
 (0)