javascript - Is it necessary to use key derivation when using Node's 'crypto' module? - Stack Overflow

I need to implement symmetric encryption using a user's passphrase in a NodeJS application. When u

I need to implement symmetric encryption using a user's passphrase in a NodeJS application. When using crypto.createCipheriv(), do I need to perform some sort of key derivation on the passphrase to obtain a value for the key parameter, or is it sufficient to just pass the user's passphrase as-is and this is taken care of by the implementation?

I need to implement symmetric encryption using a user's passphrase in a NodeJS application. When using crypto.createCipheriv(), do I need to perform some sort of key derivation on the passphrase to obtain a value for the key parameter, or is it sufficient to just pass the user's passphrase as-is and this is taken care of by the implementation?

Share Improve this question asked Mar 5, 2019 at 2:07 millimoosemillimoose 40k11 gold badges87 silver badges138 bronze badges 6
  • Im not entirely understanding, what you want to do. you want to create a symmetric encryption using a passphrase of the user, and you will have it on the server side so will be able to use it as a seed in both sides? I'm not following what are you willing to do with these pieces. Are you willing to use a key separation technique or you just want to successfully and securely get a symmetric key? – Daniel Vega Commented Mar 7, 2019 at 15:42
  • 1 createCipheriv() doesn't modify the user specified key, so yes, if you have a password you should use a KDF. – t.m.adam Commented Mar 7, 2019 at 18:08
  • @DanielVega - no, this is an Electron app and encryption is for data being synchronized – millimoose Commented Mar 7, 2019 at 19:08
  • @t.m.adam Thanks, that’s probably closest to what I’m looking for. Is my intuition that not stretching the passphrase is a Bad Idea correct? (My current thinking is to shove the passphrase and the user ID - both generated using nanoid that claims to produce sound character distributions from a CSPRNG - into Argon2; with only the ID ever existing outside the user’s device as cleartext, the derived key cached on it, the passphrase as close to never as is possible on the platform.) – millimoose Commented Mar 8, 2019 at 18:32
  • 1 Yes, never use a password as key. Argon2 is a very good KDF, but it's not supported by crypto as far as I know. I'll post an answer using PBKDF2/scrypt if you're interested. – t.m.adam Commented Mar 8, 2019 at 18:45
 |  Show 1 more ment

2 Answers 2

Reset to default 9 +100

Passwords should not be used as keys directly, but they can be used to produce a key with a KDF. That is because a key is expected to have a certain size and because passwords are weak - they use only a limited set of bytes and they usually contain words. This makes them vulnerable to both brute-force and dictionary attacks. KDFs not only produce long and uniform keys, but they also introduce a work factor which makes brute-force attacks impractical.

createCipheriv() does not modify the key contents or size. This is not mentioned in the documentation, but following the source code (from createCipheriv: source, to Cipheriv: source, to createCipherWithIV: source, to prepareSecretKey: source) we see that the key is used as it is. So, the key is expected to have the right size and it should have enough plexity.

Crypto provides two password-based KDFs, scrypt and PBKDF2. It would be best to use scrypt because it is very expensive in terms of CPU and memory resources and it can be adjusted for parallel processing, while PBKDF2 only costs CPU resources. Both KDFs require a salt, which should be long and random.

Creating a key with scrypt:

const keySize = 16; // for AES-128
const salt = crypto.randomBytes(16);
const key = crypto.scryptSync('password', salt, keySize);

Creating a key with pbkdf2:

const keySize = 16; // for AES-128
const salt = crypto.randomBytes(16);
const key = crypto.pbkdf2Sync('password', salt, 10000, keySize, 'sha256');

Where 'sha256' is the underlying hash and 10000 is the remend minimum number of iterations, that determines the work factor. In scrypt the general work factor is an optional parameter with default value 16384 (2^14), and can be set in options['cost'], where we can also set the block size and parallelization. Those values can be increased quite a lot, depending on the OS; each operation should take about 100 ms.

Finally, Argon2 is considered to be a very good KDF and like scrypt it can be adjusted for CPU and memory consumption and parallel processing. Although Argon2 it is not available in crypto, it is provided by other Node.js packages.

The difficulty of breaking a cipher depends more on the used algorithm than the length of the password. (omitting brut-force attacks)

The key extension does not increase security, because you still have the same short password at the begining. For your safety, immediately assume that you had a break-in and application code was leaked. That is, all standard and custom algorithms are public.

And you will have to use the password extension anyway, because most of the algorithms require a secret with a specific length.


An earlier answer, a little off topic.

TL;DR: The best way is to process the password into a binary buffer (string).

The symmetric encryption is precisely based on the fact that having the secret you are able to perform the reverse operation. Symetric mean operations are reversible.

Your programming language, framework or library does not matter.

Some differences are at the stage of packaging the encrypted message. You can receive a raw message, or a beautifully formatted message, where you have added IV and content in base64.

You also have to process the key in the same way. But it's about coding big endian and little endian and character encoding eg: utf-8, latin2

IV is an additional protection that aims to generate different encrypted messages for the same ining message and secret. But as it is written in the last paragraph of the section you are targeting:

They do not have to be secret: IVs are typically just added to ciphertext messages unencrypted.

In summary: you do not need to affect the encryption process itself, and you need to check what data the Electron needs.

For example my decrypt procedure (in node, data from PHP):

let crypto = {
  key: Buffer.from('secret in hex', 'hex'),
  cipher: 'aes-128-cbc',
  iv_size: 16
}
    let bData = Buffer.from(data.replace(/ /g,'+'), 'base64');
    let iv = bData.slice(0, crypto.iv_size);
    let text = bData.slice(crypto.iv_size);
    var decipher = crypt.createDecipheriv(crypto.cipher, crypto.key, iv);
    decipher.setAutoPadding(false);
    var decrypted = decipher.update(text,'hex','hex');
    decrypted += decipher.final('hex');

For PHP encrypt:


    $data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv);

    // Use base64 encoding to convert to a string
    return base64_encode($iv.$data);

//where
array(
        'key'=> urldecode('secret in urlencode'),
        'cipher' => MCRYPT_RIJNDAEL_128,
        'mode'   => MCRYPT_MODE_CBC,
       ),

As you can see, I had to find the encryption algorithms implemented on both platforms, and provide a way to provide an identical key on both platforms.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745238484a4618033.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信