Summary
hg Use ID
Chacha20 and BLAKE2s JavaScript implementation
Download as zip
Laman e2e6fc598575
3 years ago
Laman ab4439e73394
3 years ago
Laman 07d399b4c31c
3 years ago
Laman e2095e3881bf
3 years ago
Laman abd03aa8b163
5 years ago
Laman 2f53bcd63dfa
5 years ago
Laman 40f2dda94216
5 years ago
Laman 20fa1614ab45
5 years ago
Laman 5c8fceb4e291
5 years ago
Laman a9595f817d39
5 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

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.