node icon indicating copy to clipboard operation
node copied to clipboard

Customize password-based KDF in `KeyObject#export`

Open Phoenix35 opened this issue 8 months ago • 2 comments

What is the problem this feature will solve?

At the moment, node.js uses OpenSSL defaults when exporting a key object

const { privateKey } = generateKeyPairSync("ec", {
  namedCurve: "P-384",
  privateKeyEncoding: {
    type: "pkcs8",
    format: "pem",
    cipher: "aes-256-cbc",
    passphrase: "something secret",
  },
});

generates a PEM-encoded encrypted private key with 2048 iterations of PBKDF2-HMAC-SHA256. OWASP recommends way more (600k).

What is the feature you are proposing to solve the problem?

In OpenSSL CLI openssl pkcs8 -topk8, the iterations are customizable with the -iter option. Alternatively, one can choose -scrypt (and relevant -scrypt_N/r/p params) as the KDF in OpenSSL but not in node.js. The PRF can also be a different HMAC-SHAxxx but that's less relevant in my opinion

I open the discussion for extending KeyObject#export and related interfaces with one of the following

  1. A derivedKey parameter that excludes passphrase. In this case, no KDF is performed, and it is strongly emphasised to pass a key derived with scrypt, pbkdf2, or argon2id (node.js support one day :crossed_fingers:) (EDIT: support for argon2id introduced in OpenSSL 3.2.0)
privateKeyEncoding: {
  ...
  cipher: "aes-256-cbc",
  derivedKey: await promisifiedScrypt(weakUserPassphrase, salt, 32, { p: 5 }) // {@link <https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#scrypt>}
}
  1. Extend cipher to be an object
cipher: {
  alg: "aes-256-cbc",
  kdf: "pbkdf2",
  iter: 600_000,
  /* prf: "hmacWithSHA256" */
  
  // mutually exclusive
  alg: "aes-256-cbc",
  kdf: "scrypt",
  cost: ...,
  // same options as with crypto.scrypt
}
  1. Change the default iterations count to follow OWASP guidelines. This loses parity with OpenSSL defaults, which can be confusing for users, and there is no extensibility in case OWASP removes pbkdf2 from its recommandation. I am strongly against it, but its implementation would seem the less painful of all

What alternatives have you considered?

Install OpenSSL on operating systems that don't provide it (hello Windows) and encrypt with its CLI (via child_process or a terminal)

Phoenix35 avatar Jun 05 '24 22:06 Phoenix35