Diffie-Hellman Key Exchange implemented in lua

What is Diffie-Hellman key exchange?

Diffie-Hellman key exchange is a mathematical method/algorithm to establish a secure connection between two parties over an untrusted channel through the creation of a shared secret between them. The exchange is done in such a way so the server can’t see the shared secret, meaning that any messages encrypted through it are only readable by the intended recipients(and not the server). Diffie-Hellman key exchange is often used in cryptography for things such as end to end encryption.

What are the mathematics?

The mathematics of Diffie-Hellman are defined as following:

  • Private Key = random bytes
  • Public Key = Generator^PrivateKey mod prime
  • SharedSecret = OtherPublicKey^PrivateKey mod prime

The computed shared secret is the same value between both parties, and if it gets computed on the client the server doesn’t have access to it

Hyperparameters

In this implementation of Diffie-Hellman, the hyperparameters used are:

  1. The 2048-bit prime mentioned in rfc3526
  2. A generator of value 2
  3. 256-bit private keys

What issues does the module have?

Unfortunately, lua, unlike some other programming languages(for example python), doesn’t provide exact large integer arithmetic. More specifically lua integers stop being precise after their value surpasses 2^53. To counteract this, I had to handle calculations manually, this was done by doing calculations on lists of smaller numbers(each having a maximum value of 2^24), and this in turn caused significant increase in processing time. I tried optimizing the module, and I managed to drop the processing time from 20 seconds per modular exponentiation to around 1.6 seconds. This means that for the total key exchange process, around 6-7 seconds are needed, which I still consider a lot.

Other than that the module hasn’t been tested against side-channel attacks or attacks of any other form, and probably contains many vulnerabilities related to my code and not the encryption scheme itself. However the module does contain the option to create non-pseudorandom private keys through the use of API calls to random.org

API

The module currently contains the following functions:

  • getP() which returns the prime number being used
  • getGenerator() which returns the generator value being used
  • hexify(bigNum) which converts the number list to a printable hex string
  • computePrivateKey(trueRandom: boolean?) which computes the random private key, and if true is passed in, true randomness fetched from random.org is used instead of pseudorandom values
  • computePublicKey(privateKey: bigNum) that computes the public key of the user that is supposed to be sent over the network from their private key(that they keep secret)
  • computeSharedSecret(privateKey: bigNum, publicKey: bigNum) that computes the shared secret between the two parties, this function is meant to be used on the client where the arguments being passed in is the secret private key and the shared public key of the other recipient.

Example Usage

--Require the module
local DH = require(script.DiffieHellman)

local prime = DH.getP()
print("Prime:", DH.hexify(prime))

--When true is passed in the function, true randomness is used
local privateKey = DH.computePrivateKey(true)
local privateKey2 = DH.computePrivateKey(true)

--Private keys are meant to be calculated on the client
--We do everything on the server here for example simplicity 
print("Bob's private key:", DH.hexify(privateKey))
print("Alice's private key:", DH.hexify(privateKey2))

local publicKey = DH.computePublicKey(privateKey)
local publicKey2 = DH.computePublicKey(privateKey2)

print("Bob's public key:", DH.hexify(publicKey))
print("Alice's public key:", DH.hexify(publicKey2))

local sharedSecret1 = DH.computeSharedSecret(privateKey, publicKey2)
local sharedSecret2 = DH.computeSharedSecret(privateKey2, publicKey)

print("Bob's secret:", DH.hexify(sharedSecret1))
print("Alice's secret:", DH.hexify(sharedSecret2))

local successful = DH.hexify(sharedSecret1) == DH.hexify(sharedSecret2)

print("Algorithm ran successfully:", successful)

Example Output

Other Uses

Apart from communication between clients, this module can also be used for secure communications between servers in cross-server messaging contexts where the servers are treated as if they’re talking clients and the server in between is Roblox systems.

Where to Find

The module is distributed to the creator store and available through the following link:

It can also be used by simply requiring the module through the id:

local DH = require(98804489109160)

Any optimizations and contributions to the code are highly appreciated.

8 Likes

Great module! Have you tried using buffers to optimize processing time?