Is a modulo addition safe for the encryption of private keys? 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.
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:


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.
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).
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.