Laman | 9f2498ffdd4e |
5 years ago
|
|||
Laman | 63e17c1c419e |
5 years ago
|
|||
Laman | d27d9e2dd943 |
6 years ago
|
|||
Laman | 0c6c972353fe |
6 years ago
|
|||
Laman | e1ac6a6fd451 |
6 years ago
|
|||
Laman | c0f165783af4 |
6 years ago
|
CryptoJS
Javascript implementation of the Chacha20 cipher and BLAKE2s hash function. I wrote it for my amusement. Licensed under GNU GPL.
I trust the output messages to be secure and reliably authenticated. But it is up to the potential user to trust my skills and my honesty if he decides to use this code. For serious purposes you can find more serious software.
Components
Chacha20
A stream cipher as documented in RFC 8439. It is used to encrypt the message.
BLAKE2s
Cryptographic hash function as documented in RFC 7693. Resistant to length extension attacks. It is used for key stretching and message authentication.
PBKDF2-BLAKE2s
PBKDF2 key derivation function as documented in RFC 2898. It aims to make brute forcing a message key ineffective. This implementation uses plain BLAKE2s (no HMAC) as the underlying hash function. It is used for the initial key stretching.
Encryption scheme
A 32 bytes encryption key is derived from the user provided password and a 12 bytes random salt with PBKDF2 (variable number of rounds, 0.5-1 s). It is then used to encrypt the plaintext with Chacha20 and sign the message along with necessary auxilliary data with BLAKE2s. The final message consists of 30 bytes header: 1 B program version, 1 B indicating number of PBKDF rounds, 16 B signature and 12 B salt; followed by the encrypted message.
Discussion
- Using the salt in the key derivation and as the input to Chacha20 is not strictly necessary, as the cipher is already implicitly salted by the derived key. But there is no reason not to include it.
- Implementing a computation heavy function like the PBKDF in unoptimized Javascript code severly hinders its performance. Therefore its protection of weak passwords is rather limited and can't be relied upon. Then again, on my computer it achieves several thousand iterations within the alotted time, which I consider somewhat beneficial.
- It is necessary to first derive the key before the signature can be checked. Therefore it is trivial to create messages that will take impractically long to reject when injected into genuine communication (by setting a high N). Signing with the plain password to mitigate this would remove the protection of key derivation function against brute forcing. Setting the PBKDF to a fixed number of rounds would unavoidably make it unoptimal on some devices (either high performance or low performance). So I deem the current situation acceptable.
- 30 bytes header can make very short messages a lot longer. For special purposes it would be possible to remove the signature (16 B) or even the header altogether and rely just on the password (now with the implied condition of using a unique password each time). Acknowledging this, I still consider it wiser to always include the header for a general case.
Use
Install the dependencies with npm install
.
Then you can test the package with Jasmine: npm test
and bundle it with Rollup: npx rollup -c
.
Import with <script type="text/javascript" src="main.js"></script>
and you can use cryptoJS.encrypt(plaintext, password)
, cryptoJS.decrypt(ciphertext, password)
and the rest of objects and functions available from src/main.js
.