Search code examples
node.jsencryptiondiffie-hellman

How to use Diffie-Hellman with 2048 bits to create a cipher?


I'm trying to find an algorithm within the list that accommodates 2048 bit length with using crypto.createDiffieHellman(2048). In other words, I have Alice and Bob using their corresponding secret keys to encrypt/decrypt messages to each other.

const crypto = require('crypto'),
      assert = require('assert'),
      algorithm = 'aes-256-cbc',
      IV_LENGTH = 16,
      DH_LENGTH = 2048;

const alice = crypto.createDiffieHellman(DH_LENGTH);
const aliceKey = alice.generateKeys();

const bob = crypto.createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bobKey = bob.generateKeys();

const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey); // should be same as aliceSecret

const password = aliceSecret;
const iv = crypto.randomBytes(IV_LENGTH).toString('hex').slice(0, IV_LENGTH);

function encrypt(text){
  const cipher = crypto.createCipheriv(algorithm, password, iv)
  const crypted = `${cipher.update(text,'utf8','hex')}${cipher.final('hex')}`
  return crypted;
}


function decrypt(text){
  const decipher = crypto.createDecipheriv(algorithm, password, iv)
  const dec = `${decipher.update(text,'hex','utf8')}${decipher.final('utf8')}`
  return dec;
}

const msg =  encrypt('Test');

const decryptedMsg = decrypt(msg)

console.log(msg, decryptedMsg);

This throws an error Invalid key length. One way to fix this is to do DH_LENGTH = 256. However, this is not a good idea with recommended minimum length being 2048 bits. Now, I can create a key with 2048 and do a slice on a length of 256 but how is this any different from doing a 256 bit DH. Basically the attacker having to guess the first/last 256 bits.


Solution

  • You are right, you should stick to the recommended size for DHKE. A common way is using a key derivation function on the output of the Diffie-Hellman Key Exchange.

    HKDF is fine for you. HKDF follows the "extract-then-expand" paradigm and, normally, the expand would be enough if you find and implementation that gives access to those functions. The below is from futoin-hkdf;

    const hkdf = require('futoin-hkdf');
    
    // Parameter overview
    //-------------------
    // initial keying material
    const ikm = 'string-or-buffer';
    // required output length in bytes
    const length = 32;
    // can be empty string or false equivalent
    const salt = 'strongly-encouraged';
    // optional parameter
    const info = 'optional-context';
    // HMAC hashing algorithm to use
    const hash = 'SHA-256';
    
    // Generic derivation
    //-------------------
    hkdf(ikm, length, {salt, info, hash}); // Buffer(length) - derived key
    

    IKM is your derived key, and please don't call it passwords. It is more than that. Naming like sharedKey or exchangedKey is better.

    The optional context can be used for domain separation so that you can derive different keys for different applications. See the datils here; Multiple AES Key Derivation from a master key

    And, for forward secrecy, don't forget to erase the key after use.

    • Why should we process the result of the Diffie-Hellman Key Exchange?

    The security of Diffie-Hellman key exchange is based on the Decisional Diffie-Hellman assumption. This assumption says that the exchanged key is a group element that is computationally indistinguishable from a random/uniformly distributed element in the group.

    One must note that the result is not uniformly distributed element, i.e. each bit has 1/2 probability for being 0 or 1. The MSB may not be uniformly distributed.

    The recommended way to extract the extropy is by using a hash or better to be processed by a Key Derivation Function. HKDF would be fine here.