# A simple block cipher based on the SHA-256 hash function

I've come up with this little routine for doing encryption using the SHA-2 (in this case SHA-256) hash function. As such it is a block cipher with a 256 bit (32 byte) block size and an arbitrary key length. It's easy to see that you could replace the hash function and get an entirely new type of algorithm.

I want to know if there are any problems, especially flaws, in this construction, and whether algorithms of this type have been studied.

Initialization with key:

$$S_0 = H(k)$$

Encryption for plaintext blocks $P_1, \dots P_n$:

$$S_i = H(S_{i-1})$$ $$C_i = P_i \oplus S_i$$

Decryption for ciphertext blocks $C_1 \dots C_n$:

$$S_i = H(S_{i-1})$$ $$P_i = C_i \oplus S_i$$

Here is some sample C source code using SSL:

#include <openssl/sha.h>

struct sha_crypt_state {
unsigned char digest[SHA256_DIGEST_LENGTH];
};

void sha_crypt_set_key( sha_crypt_state *state, unsigned char *key, int key_length )
{
SHA256( key, key_length, state->digest );
}

void sha_crypt( sha_crypt_state *state, unsigned char *block )
{
sha_crypt_state temp;
SHA256( state->digest, sizeof(state->digest), temp.digest );
memcpy( state->digest, temp.digest, sizeof(temp.digest) );

for ( int i = 0; i < sizeof(state->digest); ++i ) {
block[i] ^= state->digest[i];
}
}

void sha_crypt_test()
{
const char *key = "secret";

// prepare a test block
char block[SHA256_DIGEST_LENGTH];
memset( block, 0, sizeof(block) );
strcpy( block, "Hello, testing encryption!" );

// test encrypt
sha_crypt_state state;
set_key( &state, (unsigned char *)key, strlen(key) );
sha_crypt( &state, (unsigned char *)block );

// test decrypt
set_key( &state, (unsigned char *)key, strlen(key) );
sha_crypt( &state, (unsigned char *)block );
}

-
Why do you call this a block cypher? Looks like a stream cypher to me. – CodesInChaos Jan 20 '12 at 12:45
Welcome to cryptography Stack Exchange. We want our questions here a bit more on the theoretical level, i.e. mostly without any actual source code, and mathematical formulas instead. I'll edit your question to be more conform with this idea. – Paŭlo Ebermann Jan 20 '12 at 12:47
This is almost the exact same algorithm as listed on this question: crypto.stackexchange.com/questions/48/… The answers there also provide some pretty good insight. – John Gietzen Jan 20 '12 at 17:31

Your cipher looks a bit like the output feedback mode of operation for block ciphers.

While OFB for block ciphers is considered safe (as long as it is used right), OFB for a hash function like you are using it has the problem that the key is only used at the start, to generate the "initialization vector", not at each step of the algorithm.

Thus, as CodeInChaos remarked, your code is vulnerable to a known-plaintext attack: Given one block of corresponding plaintext and ciphertext, we can derive the corresponding state of the cipher, and from this calculate all following states (and thus decrypt the rest of the message, though not blocks before the start).

Also, your scheme has no initialization vector, i.e. reusing the same key produces the same key stream.

I think you can get a secure version by including the key at each step of the hashing, and also including a proper initialization vector at the start, using this function for the state update:

$$S_0 = H(K || IV )$$ $$S_i = H(K || S_{i-1})$$

-
 Thanks for your response. Can someone verify that his proposal actually fixes the algorithm and prevents the known-plaintext attack? – user1449 Jan 20 '12 at 13:16 For some more elaboration, have a look at an earlier question about this construction, Is it feasible to build a stream cipher from a cryptographic hash function? – Paŭlo Ebermann♦ Jan 20 '12 at 13:21

First of all, this no block cypher at all. It's a stream cypher. Thus you can use every key only once, and you can't use any cypher modes built on block cyphers.

Your scheme is vulnerable to a known plaintext attack. If the attacker knows 32 aligned(or 63 unaligned) bytes of plaintext, he can calculate the state of your cypher:

$S_i = P_i \oplus C_i$

The later $S_j (j>i)$ only depend on the $S_i$ the attacker recovered, i.e. he can calculate all of them. And thus he can decrypt everything that comes after that known plaintext.

Such a plain text attack is often possible in practice. Files often start with known magic bytes, network protocols often contain known sequences in headers(http headers should be long enough),...

-
And to say things more generally, that kind of stream cipher is secure only if the stream would be a good PRNG. A PRNG should not allow recomputing its internal state from its output (because knowing the state implies being able to predict subsequent output, and a secure PRNG must offer prediction resistance). It is doable with hash functions, but needs more effort. See NIST SP800-90 for some hash-based PRNG (Hash_DRBG and HMAC_DRBG). – Thomas Pornin Jan 20 '12 at 13:08

If I understood your code correctly, what you are doing is encrypting a message $m$ with a key $k$ by: $c=m\oplus h(k)$, in an ECB mode where $h$ is some hash function. Take two encrypted blocks $c_1$ and $c_2$ and add them: $c_1\oplus c_2 = m_1 \oplus h(k) \oplus m_2 \oplus h(k)=m_1\oplus m_2$.

Moreover, you may loose entropy if the initial secret is selected in a smaller subset than 2^256 hash output values (in the case of a 256-bit hash function).

-
 It's not ECB mode. The state is maintained between successive calls to the encryption routine. In your notation: $c_1\oplus c_2 = m_1 \oplus h(k) \oplus m_2 \oplus h(h(k))$ – user1449 Jan 20 '12 at 11:52