Search code examples
node.jscryptographyelectrondiffie-hellman

Electron run cryptographic Diffie Hellman Key generation on a seperate thread


In my electron application I create Diffie-Hellman keys via the following method:

const crypto = require('crypto');

/**
 * Generate the keys and the diffie hellman key agreement object.
 * @param {Integer} p The prime for Diffie Hellman Key Generation
 * @param {Integer} g The generator for Diffie Hellman Key Exchange
 */
async function createSelfKey(p, g, callback) {
  let returnVal = null;
  if (p && g) {
    returnVal = { dh: await crypto.createDiffieHellman(p, g) };
  } else {
    returnVal = { dh: await crypto.createDiffieHellman(2048) };
  }
  returnVal.keys = await returnVal.dh.generateKeys();
  return callback(returnVal);
};

But the key generation is a slightly computation-heavy process thus it makes my application to freeze. An example of usage is when I try to implement this method generateCreatorKeys from the following function:

function ChatRoomStatus() {
  /**
   * @var {Object}
   */
  const chatrooms = {};

  // Some other logic
    /**
   * This Method fetched the creator of the Chatroom and executes a callback on it.
   * @param {String} chatroom The chatroom to fetch the creator
   * @param {Function} callback The callback of the chatroom.
   */
  this.processCreator = (chatroom, callback) => {
    const index = _.findIndex(chatrooms[chatroom].friends, (friend) => friend.creator);
    return callback(chatrooms[chatroom].friends[index], index , chatrooms[chatroom] );
  };


  /**
   * Generate keys for the Chatroom Creator:
   * @param {String} chatroom The chatroom to fetch the creator
   * @param {Function} callback The callback of the chatroom.
   */
  this.generateCreatorKeys =  (chatroom, callback) => {
    return this.processCreator(chatroom, (friend, index, chatroom) => {
       return createSelfKey(null, null, (cryptoValues) => {
        friend.encryption = cryptoValues;
        return callback(friend, index, chatroom);
       });
    });
  };
};

An example that this method is called is:

const { xml, jid } = require('@xmpp/client');

/**
 * Handling the message Exchange for group Key agreement 
 * @param {Function} sendMessageCallback 
 * @param {ChatRoomStatus} ChatroomWithParticipants 
 */
function GroupKeyAgreement(sendMessageCallback, ChatroomWithParticipants) {
  const self = this;
  /**
   * Send the Owner participant Keys into the Chatroom
   */
  self.sendSelfKeys = (chatroomJid, chatroomName) => {
    ChatroomWithParticipants.generateCreatorKeys(chatroomName, (creator) => {
      const message = xml('message', { to: jid(chatroomJid).bare().toString()+"/"+creator.nick });
      const extention = xml('x', { xmlns: 'http://pcmagas.tk/gkePlusp#intiator_key' });
      extention.append(xml('p', {}, creator.encryption.dh.getPrime().toString('hex')));
      extention.append(xml('g', {}, creator.encryption.dh.getGenerator().toString('hex')));
      extention.append(xml('pubKey', {}, creator.encryption.keys.toString('hex')));
      message.append(extention);
      sendMessageCallback(message);
    });
  };
};

module.exports = GroupKeyAgreement;

Do you know how I can "run" the function createSelfKey in parallel/seperate thread and serve its contents via a callback? Also the code above runs on Electron's main process thus a freeze on it causes the whole application to stall for a while.


Solution

  • The best solution I tried to your problem is the following code based upon answer:

    const crypto = require('crypto');
    const spawn = require('threads').spawn;
    
    /**
     * Generate the keys and the diffie hellman key agreement object.
     * @param {Integer} p The prime for Diffie Hellman Key Generation
     * @param {Integer} g The generator for Diffie Hellman Key Exchange
     * @param {Function} callback The callback in order to provide the keys and the diffie-hellman Object.
     */
    const createSelfKey = (p, g, callback) => {
    
      const thread = spawn(function(input, done) {
        const cryptot = require('crypto');
        console.log(input);
        const pVal = input.p;
        const gVal = input.g;
        let dh = null;
    
        if (pVal && gVal) {
          dh = cryptot.createDiffieHellman(pVal, gVal);
        } else {
          dh = cryptot.createDiffieHellman(2048);
        }
    
        const pubKey = dh.generateKeys();
        const signaturePubKey = dh.generateKeys();
        done({ prime: dh.getPrime().toString('hex'), generator: dh.getGenerator().toString('hex'), pubKey, signaturePubKey});
      });
    
      return thread.send({p,g}).on('message', (response) => {
        callback( crypto.createDiffieHellman(response.prime, response.generator), response.pubKey, response.signaturePubKey);
        thread.kill();
      }).on('error', (err)=>{
        console.error(err);
      }).on('exit', function() {
        console.log('Worker has been terminated.');
      });
    };
    

    As you can see using the threads library from npm will provide you what you need. The only negative on this approach is that you cannot pass the in-thread generated objects outside the thread's scope. Also the code that is inside the function executing the thread is an some sort of an isolated one thus you may need to re-include any library you need as you can see above.