I'm writing a JavaScript library which needs to encrypt 256-bit private keys. The algorithm often used for that is AES, but the shortest implementation around has 172 lines of code.

I'm trying to optimize for code size, so I'm looking for shorter alternatives. I've done some research and believe this should work well enough:

encrypt(key, password):
    salt = random64Bits()
    hash = keccak256(salt + password)
    enc = []
    for i from 0 til 32:
        enc[i] = (key[i] + hash[i]) % 16
    return enc + salt

I.e., the modulo-16 addition of each byte of the password and the hash of (key + a random salt). It can be implemented in about 10 lines, so, if there isn't anything inherently wrong, I'd certainly favor it.

Is this encryption scheme secure? Are there equally simpler alternatives properly standardized?

Note that I already require Keccak for other functionality, so the Keccak code base doesn't count to the amount of code required.

  • $\begingroup$ So you can use keccak but not AES? which other primitives do you have? $\endgroup$ – SEJPM Apr 19 '17 at 12:17
  • $\begingroup$ @SEJPM oh, sorry, that is the point, I already have Keccak (which I heavily pruned from the existing libs), so I thought I could use it to make a shorter encryption. Keccak is all I have, actually. $\endgroup$ – TreeHaunter Apr 19 '17 at 12:18
  • $\begingroup$ The code looks goofy. Is the key in binary (that is, a 256 bit key would be 32 bytes)? If so, why are you iterating from 0 to 256? If so, why are you computing the addition modulo 16 (which would discard the upper nybble)? $\endgroup$ – poncho Apr 19 '17 at 12:20
  • $\begingroup$ OK, the informal problems with this are: 1) the password-based key derivation is very prone to brute-force attacks and 2) the scheme lacks any form of authentication otherwise this is highly unconventional but should be fine. The canonical solution to this would be some variation of CTR mode paired with a Keccak-MAC and iterated keccak for PW hashing. $\endgroup$ – SEJPM Apr 19 '17 at 12:20
  • 1
    $\begingroup$ @treehaunter a simple salted hash allows for many tries very quickly, a proper implementation should use at the very least an iterated hash and if possible a dedicated password hashing function, to slow password-guessing down. $\endgroup$ – SEJPM Apr 19 '17 at 12:23

Note: I will not talk about potential implementation flaws in this answer but rather about the conceptual way to approach this problem.

OK, so what you have a 256-bit hash function $H$ (Keccak) and what you need is a (somewhat) secure password-based encryption scheme that from what I could guess avoids operations on bit-wise data.

So what should be used is really CTR-mode with a Keccak-MAC and an iterated Keccak for password-based key-derivation purposes. I shall now discuss these three components:

  1. Iterated Keccak PBKDF. Right now the code does $k\gets H(s\parallel p)$. This means an attacker has to do exactly one evaluation of the (fast) hash function to verify a password guess. This is way too fast, as users tend to choose weak passwords which then get brute-forced quickly. If you want to read further into this, this is basically equivalent to "salted, plain MD5". Now for the solution: As you seemingly can't afford to use a proper password hashing scheme, your best choice is to use PBKDF2 with Keccak as your PRF, ie your PRF would be $F(k,x)\mapsto H(k\parallel x)$. I expect this to only take a few lines and you can re-use your salt. As for the iteration count, you should adjust it such that the derivation takes roughly 100ms.
  2. Keccak-CTR. Right now your code iterates over all nibbles and adds the "noise" that was generated by the hash function call. This is safe (in theory), however it only works as long as the output of the hash function and the size of the data object in question match. If they don't anymore, you really want to use CTR-mode, or rather want to use $c_i\gets p_i\oplus H(k\parallel i)$ for blocks of the size of a hash function output. I also have to recommend against using the key directly as the "pad" for the data, but rather that it is run through the hash function once more, the reason being cryptographic key separation (and it doesn't scale as mentioned above).
  3. Keccak-MAC. Right now you wouldn't nice manipulations on the key, which could potentially lead to keys being more easily attackable or just "simple" denial-of-service attacks. This is why you should use cryptographic authentication. Doing so is actually quite simple, due to Keccak being a sponge construction: $\tau\gets H(k\parallel s\parallel c)$, ie the authentication tag is the result of the concatenated hash of the key, the salt and the cipher text. Append this to your output and verify the consistency of this value on loading and you can rest assured that it hasn't been tampered with.
  • $\begingroup$ Very good points, thank you. I'll spend some time researching the methods suggested here now. (Note: I'll use Wikipedia mainly, if there is a better resource let me know.) $\endgroup$ – TreeHaunter Apr 19 '17 at 13:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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