user29212913
user29212913

Reputation: 37

ECDH Exchange in Go, NodeJS, and Lua

Go and NodeJS produced the same result, but not Lua. I want Lua to produce the same one.

package main

import (
    "crypto/ecdh"
    "encoding/base64"
    "log"
    "os"

    "github.com/joho/godotenv"
)

func main() {
    _ = godotenv.Load()

    aDecodedPrivateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("A_PRIVATE_KEY"))
    bDecodedPrivateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("B_PRIVATE_KEY"))

    aPrivateKey, _ := ecdh.P256().NewPrivateKey(aDecodedPrivateKey)
    bPrivateKey, _ := ecdh.P256().NewPrivateKey(bDecodedPrivateKey)

    sharedSecret, _ := aPrivateKey.ECDH(bPrivateKey.PublicKey())

    log.Println(base64.StdEncoding.EncodeToString(sharedSecret))
}
require('dotenv').config()

import { createECDH } from 'crypto'

const aKey = createECDH('prime256v1')
const bKey = createECDH('prime256v1')

aKey.setPrivateKey(Buffer.from(String(process.env.A_PRIVATE_KEY), 'base64'))
bKey.setPrivateKey(Buffer.from(String(process.env.B_PRIVATE_KEY), 'base64'))

console.log(aKey.computeSecret(bKey.getPublicKey()).toString('base64'))
local openssl_pkey = require "resty.openssl.pkey"
local openssl_bn = require "resty.openssl.bn"
local ngx = require "ngx"

local function ecdh()
  local a_decoded_key = ngx.decode_base64(a_private_key)
  local b_decoded_key = ngx.decode_base64(b_private_key)
  local a_bn = openssl_bn.new(a_decoded_key, 2)
  local b_bn = openssl_bn.new(b_decoded_key, 2)

  local a_key = openssl_pkey.new({
    type = "EC",
    params = {
      private = a_bn,
      group = "prime256v1"
    }
  })
  local b_key = openssl_pkey.new({
    type = "EC",
    params = {
      private = b_bn,
      group = "prime256v1"
    }
  })
  local b_public_key = openssl_pkey.new(b_key:to_PEM('public'))

  return ngx.encode_base64(a_key:derive(b_public_key))
end

Please help me create the Lua version that produces the same result as Go and NodeJS.

In case you need environment variables, take this .env.

A_PRIVATE_KEY="w2UwuwmF9h5p02fnr3MkxtKoDTl8aTtJXqLbsPwbqPg="
B_PRIVATE_KEY="ZyoPMal0TZzNwDyUUE30iThXCKgPOthPaIN2qnOhkNs="

Here is my testing environment.

References that might help.

Thank you in advance.

Upvotes: 1

Views: 74

Answers (2)

user29212913
user29212913

Reputation: 37

Oh, I just knew the problem. In Lua, we can not create keys from this source.

A_PRIVATE_KEY="w2UwuwmF9h5p02fnr3MkxtKoDTl8aTtJXqLbsPwbqPg="
B_PRIVATE_KEY="ZyoPMal0TZzNwDyUUE30iThXCKgPOthPaIN2qnOhkNs="

So, these syntaxes are wrong.

local a_key = openssl_pkey.new({
  type = "EC",
  params = {
    private = a_bn,
    group = "prime256v1"
  }
})
local b_key = openssl_pkey.new({
  type = "EC",
  params = {
    private = b_bn,
    group = "prime256v1"
  }
})

These AI-suggested syntaxes are also wrong.

local a_key = openssl_pkey.new({
  type = "X25519",
  curve = "prime256v1",
  private_key = a_decoded_key,
})

We need to convert the source to the PEM keys first. So, the correct source would be like this.

A_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_CONTENT_HERE\n-----END PRIVATE KEY-----"
B_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_CONTENT_HERE\n-----END PRIVATE KEY-----"

Then, the correct Lua syntax would be like this.

local a_key = openssl_pkey.new(a_private_key)
local b_key = openssl_pkey.new(b_private_key)

Upvotes: 1

I see at least three problems:

  1. X25519 and prime256v1 are totally different curves. You need to pick one or the other and use it consistently.
  2. That's not the right syntax to make a pkey with existing key material. Here's how you'd modify that part of your code to do that correctly:
local openssl_bn = require "resty.openssl.bn"
local a_bn = openssl_bn.new(a_decoded_key, 2)
local a_key = openssl_pkey.new({
  type = "EC",
  params = {
    private = a_bn,
    group = "prime256v1"
  }
})
  1. OpenSSL doesn't automatically calculate the public key from the private key, so you have to do that yourself, as How do I obtain the public key from an ECDSA private key in OpenSSL? explains. Unfortunately, lua-resty-openssl doesn't expose a lot of necessary functions for this, e.g., EC_POINT_mul, so you'd have to either switch libraries or write a lot of C FFI code yourself to do this.

Upvotes: 1

Related Questions