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...
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 &lt; 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 &lt; 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] &lt;&lt; 8) |      \
        ((u_int32_t)data[1] &lt;&lt; 16) | ((u_int32_t)data[0] &lt;&lt; 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)&amp;0x0f];                          \
    s0 = sigma0_256(s0);                            \
    s1 = W256[(j+14)&amp;0x0f];                         \
    s1 = sigma1_256(s1);                            \
    T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] +      \
         (W256[j&amp;0x0f] += s1 + W256[(j+9)&amp;0x0f] + s0);          \
    (d) += T1;                              \
    (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));            \
    j++;                                    \
} while(0)