I know there are way better and more tested encryption methods, but I was wondering if the following code could be use for symmetric encryption?

var text = "hello";
var password = "mypass";
var message = hash(text) + encrypt(text, password);

function encrypt(text, password){
    var key = hash(hash(text) XOR hash(password));
    var passphrase = key;
    while(passphrase.length < text.length){
        key = hash(key);
        passphrase += key;
    passphrase = passphrase.substring(0, text.length);
    return text XOR passphrase;

It can easily be decrypted if you know the password.

Is there any flaw to this simple algorithm?

N.B.: A similar question was asked here but the algorithm doesn't work...

  • $\begingroup$ See the question I linked as duplicate, and the accepted answer. Your construction seems weak for a similar reason. The answer also links to a more secure idea. $\endgroup$ – otus Jul 14 '14 at 13:36
  • $\begingroup$ @otus I'm not sure we have to close this question though as it describes a different algorithm than the question you pointed to. We can hack this particular one apart in our answers here. How useful that is remains questionable, I admit. $\endgroup$ – Maarten Bodewes Jul 14 '14 at 13:43
  • 1
    $\begingroup$ @owlstead, did you look at them? They only differ in how they calculate the initial block of keystream from the key. Either way, doesn't really matter to me. $\endgroup$ – otus Jul 14 '14 at 13:45
  • 2
    $\begingroup$ @otus The answer you linked was very useful but isn't his first block really insecured as the attacker know H(plaintext) and the cipherpart_0 so he can get the H(key) very easily? $\endgroup$ – Gudradain Jul 14 '14 at 14:08
  • 2
    $\begingroup$ @Gudradain One of the most obvious things is, that your algorithm does not provide any kind of authentication or data integrity checks. I’m unsure if that’s a problem or not, since you didn’t specify your exact security requirements… but since you’re using a hash here anyway, you might want to take a look at the obvious solution: add a HMAC. (In fact, you could even base your whole encryption thing on HMACs.) $\endgroup$ – e-sushi Jul 14 '14 at 15:32

The basic flaw is the same as indentified in this answer I linked in comments:

If you know the plaintext for block $i$, you can derive the key for that block and then derive the rest of the keystream from that key. Thus, an attacker can simply make guesses on the message until they find one that allows them to decrypt the rest of the message.

A better way to build a stream cipher from a hash function would be to hash a key and a counter and a unique nonce so two messages always get different keystreams and knowing a keystream block will not help in calculating the later blocks.

For example: $k_i = H(K||N||i)$

Better yet, use an existing stream cipher, or the stream cipher mode of a hash function that has one, like Skein (or IIRC SHA-3).


Here is my take on your algorithm. I'll try to perform the known plaintext attack.

  • Knowing the plaintext allows to recover the keystream (passphrase) by simply XORing it.
  • This in turn reveals the value of hash(hash(text) XOR hash(password)) -- simply the first hash block, and the value of hash(text) is known (by virtue of KPA plus it is leaked by your message construction).
  • So the attack boils down to finding a preimage for the value hash(hash(text) XOR hash(password)). It may be computationally feasible to utilise rainbow tables for that as you do not use any kind of salt for this hash. I think there should be precomputed tables for this sort of attack as they are useful in unwinding the hash(hash(hash(hash(...)))) construct as well.
  • If finding the preimage is successful, the attacker would know hash(text) XOR hash(password) candidate(s), where recovering hash(password) is trivial. Which one is the correct one can be verified by encrypting the test data.
  • At this point, the attacker would be able at least to forge arbitrary messages, as the plaintext of password is not required, only its hash.

Depends on your security requirements. Basically it is OK to create a (slow!) stream cipher using a secure hash method, given that the password has a large enough security margin.

But your code has at least the following issues:

  • you leak enough information in the first block to regenerate the key stream from a known plaintext (thanks otus);
  • you have to leak hash(text) to an attacker, otherwise you cannot decrypt;
  • you can distinguish if the same text has been encrypted twice with the same password (the encryption is deterministic as it misses an IV);
  • you don't deploy a password based key derivation funcation instead of a cryptographic hash over the password to generate the initial key.

The first one completely breaks your cipher. #2 and #3 are enough to consider the cipher broken, but may allow you to use a cipher (if it weren't for #1). #4 will make it easy for an attacker to brute-force your password, or perform a dictionary attack.

Not all hash algorithm implementations are protected against side channel attacks (less so than block ciphers anyway). And block ciphers are designed for this purpose and much faster. Stream ciphers such as Salsa20 will completely blow this implementation out of the water with regards to speed.

  • $\begingroup$ Why is it a problem to leak hash(text)? If the hash is secure the hash(text) is useless to reveal the plaintext. $\endgroup$ – Gudradain Jul 14 '14 at 13:45
  • $\begingroup$ Right. What if you have three plaintexts, "Yes", "Yes" and "No" and send them over a line? What if you send a credit card number for which only 4 digits are unknown? $\endgroup$ – Maarten Bodewes Jul 14 '14 at 13:47
  • $\begingroup$ Oh, so I was using the hash(text) as a sort of IV to protect my password, but what I really should have use is an IV (like a random number) instead of the hash(text). This answer made it clear security.stackexchange.com/questions/42642/… $\endgroup$ – Gudradain Jul 14 '14 at 14:03
  • 1
    $\begingroup$ Yup, that's it. You should also look at Password Based Key Derivation Functions instead of performing hash(password). As you may have noticed, you should only use your scheme for practicing crypto. $\endgroup$ – Maarten Bodewes Jul 14 '14 at 14:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.