invider
invider

Reputation: 353

Implementing key derivation properly as suggested by OpenSSL for crypto.createCipher() of "Crypto" library(nodejs)

I am fixing this issue. Reading the issue will give you a better picture of the problem, nonetheless I'm writing the issue here in my own language here.

This is how system should work:

User inputs text and a password, it gets saved in a file after strong encryption. When user inputs encryptedText and password, it returns the decrypted text. Currently the encryption is handled using Crypto library of node.js, function used createCipher.

Problem: The solution works very well with createCipher but it does not employ any kind of salt mechanism. Openssl recommends to use use pbkdf2 for deriving keys. I understand why it is necessary, because otherwise encrypted value of a text will be same always(e.g. encryption of "1234" with the same password will always result in "xyz"), which makes rainbow table attack easy/possible.

Solution I'm trying: I started implementing Openssl's suggestion to have key derivation mechanism using pbkdf2, but I'm not quite able to grasp how should it be done properly. Please help me understand and figure out the right approach for this issue. I am thinking of following approach but a lot of things seem to be missing

  1. deriveKey(password) : create hash for the password string using a random salt and any hashing algo("sha256" probably), iterations = 64000.
    derivedKey = crypto.pbkdf2Sync(password, salt=crypto.randomBytes(256), iterations, 512, 'sha256',...)

  2. encrypt(text, derivedKey, cb):

    • Create cipherObject using derivedKey:
      cipherObject = createCipheriv(algo="aes-256-cbc", derivedKey, iv2=randomBytes(256),...)
    • Apply encryption:
      cipherText = cipherObject.update(text,...)
    • Store encryptedText:
      1:salt;iters:IV:cipherText in a file locally (thanks to @bartonjs for making me understand this)
  3. decrypt(encryptedText, password): So the text to decrypt looks like this: 1:salt;iters:IV:cipherText
    • Get derived from the saved configuration:
      derivedKey = pbkdf2Sync(password, salt, iters, 'sha256')
    • Create decipher object with the same algo and above derivedKey: decipherObject = createDecipherIv("aes-256-cbc", derivedKey, IV...)
    • update decipherObject to get finally the original text:
      text = decipherObject.update(cipherText,...)

My 3 big questions in order of priority

  1. Am I doing everything right conceptually?
  2. Is there any security concern that I need to think of?
  3. Am I following the best practices? What could be improved.

P.S. I'm not an expert of cryptography, would be helpful to get opinion from one

Upvotes: 0

Views: 936

Answers (2)

rrb5068
rrb5068

Reputation: 81

I'm assuming you don't want to implement it yourself if you don't have to. And as CodeCaster said, if you don't have solid knowledge of it, you really shouldn't be the one implementing it.

My suggestion is to use the Stanford Javascript Encryption Library (sjcl) which has support for pbkdf2.

Check out their demo page, which allows you to encrypt a message with a password and a random salt:

https://bitwiseshiftleft.github.io/sjcl/demo/

And here's a link to the demo source, which should give you an idea of how to implement it with their library: https://github.com/bitwiseshiftleft/sjcl/blob/master/demo/example.js

Edit (bumping from the comments to an answer) As josh3736 pointed out in the comments, no third-party library is necessary. Node's crypto module natively supports pbkdf2. He provided the following link as an example: https://cafedev.org/article/2016/06/secure-text-encryption-with-nodejs/

Upvotes: 0

bartonjs
bartonjs

Reputation: 33286

For decryption I think I will have to save salt of derivedKey as well with iv. I don't know if that would compromise the security or not. What should be the solution?

A salt, from the NIST glossary:

A non-secret value that is used in a cryptographic process, usually to ensure that the results of computations for one instance cannot be reused by an Attacker.

SOURCE: SP 800-63; CNSSI-4009

Since it's defined as a "non-secret" value, writing it next to the IV does not compromise security.

Note that you also should write the iteration count for PBKDF2.

salt;iters:IV:cipherText

("Startup", "IV", "ciphertext" separated by colons, and startup is broken into "salt" and "iteration count" by a semi-colon, so parsing isn't ambiguous as the structure evolves... for best results, something in the beginning would be a "schema value", like the number 1, for v1 of your file format, like 1:salt;iters:IV:cipherText).

Upvotes: 1

Related Questions