# How is a “SHA256 reduced by X of Y rounds” built?

Wikipedia reports:

A 2011 attack breaks preimage resistance for [...] 52 out of 64 rounds for SHA-256. Pseudo-collision attack against up to 46 rounds of SHA-256.

I am having trouble determining/understanding whether the "Message Expansion" of SHA-256 is the one reduced (splitting the 512-bit message block into 16 words, and expanding them into 64 expanded message words), or the "State Update Transformation" is the one reduced (starting from the previous 256-bit chaining value, which is updated by applying the step functions 64 times).

I have the feeling that both are reduced, but I am not sure...

• I don't see how you can reduce the message expansion, let alone by a number of rounds as message expansion is only performed once, not in a number of rounds. – Maarten Bodewes Nov 21 '17 at 18:25

## 1 Answer

Taken from the OpenBSD implementation of the SHA-2 family at their CVS, where the function SHA256Transform() is used to apply a number of rounds to each block of input. It splits this into the first 16 rounds, and then the remaining ones. A reduced-round implementation of SHA-256 would simply have fewer rounds. This makes the hash significantly weaker.

The transform function:

void
SHA256Transform(u_int32_t *state, const u_int8_t *data)
{
u_int32_t   a, b, c, d, e, f, g, h, s0, s1;
u_int32_t   T1, W256[16];
int     j;

/* Initialize registers with the prev. intermediate value */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];

j = 0;
do {
/* Rounds 0 to 15 (unrolled): */
ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
} while (j < 16);

/* Now for the remaining rounds to 64: */
do {
ROUND256(a,b,c,d,e,f,g,h);
ROUND256(h,a,b,c,d,e,f,g);
ROUND256(g,h,a,b,c,d,e,f);
ROUND256(f,g,h,a,b,c,d,e);
ROUND256(e,f,g,h,a,b,c,d);
ROUND256(d,e,f,g,h,a,b,c);
ROUND256(c,d,e,f,g,h,a,b);
ROUND256(b,c,d,e,f,g,h,a);
} while (j < 64);

/* Compute the current intermediate hash value */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;

/* Clean up */
a = b = c = d = e = f = g = h = T1 = 0;
}


The round macros are declared as follows:

/* Unrolled SHA-256 round macros: */

#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do {                  \
W256[j] = (u_int32_t)data[3] | ((u_int32_t)data[2] << 8) |      \
((u_int32_t)data[1] << 16) | ((u_int32_t)data[0] << 24);        \
data += 4;                              \
T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \
(d) += T1;                              \
(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));            \
j++;                                    \
} while(0)

#define ROUND256(a,b,c,d,e,f,g,h) do {                      \
s0 = W256[(j+1)&0x0f];                          \
s0 = sigma0_256(s0);                            \
s1 = W256[(j+14)&0x0f];                         \
s1 = sigma1_256(s1);                            \
T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] +      \
(W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);          \
(d) += T1;                              \
(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));            \
j++;                                    \
} while(0)