fffonion / lua-resty-openssl

FFI-based OpenSSL binding for OpenResty
BSD 2-Clause "Simplified" License
131 stars 43 forks source link

How to verify a signature ? #166

Closed solisoft closed 5 months ago

solisoft commented 5 months ago

Hi there, I have an issue with resty.openssl.bn new method here my code

local public_key = "04bccc9cf60be6a6aa7f01f582e81559ab0789d8710c9a0d73450e999156d1aeb7c1bb45f5faafb7d27ac18b570025abdb892244cfeb0d5e15959e5a9a7af77764"

local openssl_bn = require("resty.openssl.bn")

local public_key_bn = assert(openssl_bn.new(public_key, 16))

it raise runtime error: /usr/local/openresty/lua/signature.lua:15: expect nil or a number at #1

Basically I want to convert this ruby code

group = OpenSSL::PKey::EC::Group.new(cryptographic_primitive)
key = OpenSSL::PKey::EC.new(group)
public_key_bn = OpenSSL::BN.new(public_key, 16)
key.public_key = OpenSSL::PKey::EC::Point.new(group, public_key_bn)
valid = key.dsa_verify_asn1(Base64.decode64(b64_hash), Base64.decode64(b64_signature))

any thoughts ?

fffonion commented 5 months ago

Hi @solisoft would you check the version lua-resty-openssl? The api you mentioned was added in 1.2.0. But you can also use bn.from_hex(pubkey).

fffonion commented 5 months ago

also the ruby code you referred used low level API that are not exposed in this library. simply use

pkey.new({ type = "EC", curve = "curvename" })

if you are creating a new key. there's no need to detour to bn. there'a an example if you really want to control low level details of key: https://github.com/fffonion/lua-resty-openssl/blob/master/lib/resty/openssl/auxiliary/jwk.lua#L70. You will create a EC_KEY then assign to a EVP_PKEY.

solisoft commented 5 months ago

Ok thanks I'm gonna check it

solisoft commented 5 months ago

I tried

local b64_hash = "nNVvSahaxSBqqzFwHqioVtji8EczQuieMrFRQYiM3D9aZK/hrOs8HZpcWUwNQmubT2C+yRrH1+UhG4DlyCeETw=="
local b64_signature = "MEUCIEwnBTim+8GzMuAvRhDqnOf0hUW/XW5HnUvL2dc3BV0PAiEAlnAK/dZoqHKK5xAmOJx8Hm7f1I2ueqJIMjO8fTXzPb8="
local public_key_hex = "04bccc9cf60be6a6aa7f01f582e81559ab0789d8710c9a0d73450e999156d1aeb7c1bb45f5faafb7d27ac18b570025abdb892244cfeb0d5e15959e5a9a7af77764"

local openssl_bn = require("resty.openssl.bn")
local openssl_pkey = require("resty.openssl.pkey")

-- Decode the base64 encoded hash and signature
local hash = ngx.decode_base64(b64_hash)
local signature = ngx.decode_base64(b64_signature)

-- Create a new EC key with the specified curve
local key = assert(openssl_pkey.new({ type = "EC", curve = "prime256v1" }))
key:set_parameters({ e = openssl_bn.from_hex(public_key_hex) })
-- Verify the signature
local valid = key:verify(signature, hash)

-- Print whether the signature is valid
if valid then
    ngx.say("Signature is valid")
else
    ngx.say("Signature is invalid")
end

but the key:verify returns false ... Wondering what I'm missing

fffonion commented 5 months ago

You would want to set the public component of the EC key, e is for RSA key. (It should give you error if you put assert on key:set_parameters in your code)

solisoft commented 5 months ago

You mean I can add the public key here ?

local key = assert(openssl_pkey.new({ type = "EC", curve = "prime256v1" }))
fffonion commented 5 months ago

You will do:

local key = assert(openssl_pkey.new({ type = "EC", curve = "prime256v1" }))
key:set_parameters({ public = openssl_bn.from_hex(public_key_hex) })

notice the second line above.

solisoft commented 5 months ago

I have this error now using the public attribute

[error] 6#6: *2 lua entry thread aborted: runtime error: /usr/local/openresty/site/lualib/resty/openssl/ec.lua:146: missing declaration for symbol 'EC_POINT_free'
delupay-openresty  | stack traceback:
delupay-openresty  | coroutine 0:
delupay-openresty  |    [C]: in function '__index'
delupay-openresty  |    /usr/local/openresty/site/lualib/resty/openssl/ec.lua:146: in function 'set_parameters'
solisoft commented 5 months ago

looks like the definition is missing here https://github.com/fffonion/lua-resty-openssl/blob/master/lib/resty/openssl/include/ec.lua

fffonion commented 5 months ago

You are right, let me fix that tomorrow.

fffonion commented 5 months ago

I have pushed the fix at https://github.com/fffonion/lua-resty-openssl/pull/167. I will merge that once i have my computer available.

One another issue I found is that you are trying to verify a message against a hash, while pkey:verify by default is doing ECDSA for EC keys (i.e. take take message, do digest, then do verify). So in your case you are doing verification of the digest of digest.

solisoft commented 5 months ago

I applied your fix on my docker image so yes it not crash anymore ... trying to understand "verification of the digest of digest" :)

fffonion commented 5 months ago

@solisoft So for ok, err = pk:verify(signature, message, md_alg?, padding?, opts?), it applies the digest algorithm md_alg on message then verify it with signature. What you were doing with your ruby code does not involve the message digest part, so the verification would naturely fail.

fffonion commented 5 months ago

which I think is currently a missing feature in this library, we can currently do sign_raw and verify_recover (which only works for RSA key) but doesn't have a "verify_raw" to work in your case. I will look into add that once I'm back to my computer.

solisoft commented 5 months ago

Oh thanks a lot!

fffonion commented 5 months ago

May I also ask what are the value of cryptographic_primitive in your ruby code? Try to set a matching reproducing test here.

solisoft commented 5 months ago

prime256v1

fffonion commented 5 months ago
local b64_hash = "nNVvSahaxSBqqzFwHqioVtji8EczQuieMrFRQYiM3D9aZK/hrOs8HZpcWUwNQmubT2C+yRrH1+UhG4DlyCeETw=="
local b64_signature = "MEUCIEwnBTim+8GzMuAvRhDqnOf0hUW/XW5HnUvL2dc3BV0PAiEAlnAK/dZoqHKK5xAmOJx8Hm7f1I2ueqJIMjO8fTXzPb8="
local public_key_hex = "04bccc9cf60be6a6aa7f01f582e81559ab0789d8710c9a0d73450e999156d1aeb7c1bb45f5faafb7d27ac18b570025abdb892244cfeb0d5e15959e5a9a7af77764"

local openssl_bn = require("resty.openssl.bn")
local openssl_pkey = require("resty.openssl.pkey")

-- Decode the base64 encoded hash and signature
local hash = ngx.decode_base64(b64_hash)
local signature = ngx.decode_base64(b64_signature)

-- Create a new EC key with the specified curve
local key = assert(openssl_pkey.new({ type = "EC", params = {
    group = "prime256v1",
    public = openssl_bn.from_hex(public_key_hex)
}}))
-- Verify the signature
local valid, err = key:verify_raw(signature, hash, "sha512")

-- Print whether the signature is valid
if valid then
    ngx.say("Signature is valid")
else
    ngx.say("Signature is invalid")
end

This will work for the code on linked PR. Note the change to pkey.new and verify_raw

solisoft commented 5 months ago

Awesome ! Thanks a lot, I'll wait the new opm version to test it :)

fffonion commented 5 months ago

the new opm version

Just uploaded !

solisoft commented 5 months ago

Nice! I just saw it on opm website, I'll give it a try tomorrow !

solisoft commented 5 months ago

ok actually I just gave it a try and it works like a charm ! Thanks again it will be helpful !!