Is it feasible to build a stream cipher from a cryptographic hash function? A few years ago I devised a symmetric-key system that worked like so:

H()   = some hashing function
h     = the number of bits in the result of H()
key   = bits to be used as a key
^     = the XOR operation
+     = concatenation
len() = the length function (in bits)

max = ceiling(len(cipertext) / h)
pad = pad_0 + ... + pad_max
pad_0 = H(key) ^ H(plaintext)
pad_n = H(pad_(n-1))
cipherpart_n = plaintext_n ^ pad_n
ciphertext = H(plaintext) + cipherpart_0  + ... cipherpart_max


Basically, it builds up a "pad" based on the hash of the message and hash of the key and XORs the message against it.

I am not actually an expert, so I was wondering why an approach like this isn't very common.
With the corrected system (which actually uses the key), I see these weaknesses:


If the attacker can guess some plaintext_n, he can derive pad_n from ciphertext_n and from this all the following pad_i - which means that he can read the rest of the message.
The ciphertext starts with a H(plaintext), which means that an attacker which can guess the plaintext has a means to verify this guess. To avoid this, the plaintext should at least be randomly padded. (But then you could simply use these random bytes as the initialization vector instead of the hash of the plaintext).
The entropy of your actual key stream (the pad) is reduced to h bits, even if the key itself had bigger entropy. This might be better if you input the key again to the hash function for creating the higher-order pad_n.
As already said by Jack, the need to hash the plaintext before you can start to encrypt it means that you need two passes over the plaintext, which means that the encrypting algorithm is not an online-algorithm (the decryption is).


Other than this, your algorithm is similar to the Output feedback mode of operation for Block ciphers, with the initialization vector derived from the message, and without new key input for the following steps.

Here is a variant which would not have these weaknesses (and hopefully no others):

initvec = random
pad_0 = H(key + initvec)
pad_n = H(key + pad_(n-1))

ciphertext = initvec + cipherpart_0 + ... + cipherpart_max


(The rest as in your example.)

This does not do integrity-protection ... for this you would append some keyed MAC at the end (so it can be an online algorithm).

Then we have actually the OFB mode of a strange "encryption only" block cipher derived from the hash function. The only problem would be that it would be quite slower than a normal block cipher, as Jack already mentioned.

Disclaimer:

Note that while this construction is secure in the random-oracle model (see this blog entry for a proof of a similar construction, using a counter instead of previous hash), the proof uses properties which are not implied by the collision-resistance and preimage-resistance properties of a hash function (which is what hash functions are constructed for).

So while this might work, it does not have to work with real-world hash functions.