summaryrefslogtreecommitdiffstats
path: root/server/resty/openssl
diff options
context:
space:
mode:
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>2023-04-14 11:59:32 +0000
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>2023-04-14 11:59:32 +0000
commitd68841d9f75636575cd778838a8ceea5fd5aada3 (patch)
tree778c84203ed9bfa4dc1c8234e4e2cf60da6ebd8c /server/resty/openssl
parent42af09588f1f839b9ab36356f02f34c89559bcfa (diff)
Upload ui
Issue-ID: PORTAL-1084 Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de> Change-Id: Id0c94859a775094e67b0bb9c91ca5e776a08c068
Diffstat (limited to 'server/resty/openssl')
-rw-r--r--server/resty/openssl/asn1.lua91
-rw-r--r--server/resty/openssl/auxiliary/bio.lua43
-rw-r--r--server/resty/openssl/auxiliary/ctypes.lua28
-rw-r--r--server/resty/openssl/auxiliary/jwk.lua261
-rw-r--r--server/resty/openssl/auxiliary/nginx.lua318
-rw-r--r--server/resty/openssl/auxiliary/nginx_c.lua154
-rw-r--r--server/resty/openssl/bn.lua416
-rw-r--r--server/resty/openssl/cipher.lua300
-rw-r--r--server/resty/openssl/ctx.lua78
-rw-r--r--server/resty/openssl/dh.lua142
-rw-r--r--server/resty/openssl/digest.lua116
-rw-r--r--server/resty/openssl/ec.lua186
-rw-r--r--server/resty/openssl/ecx.lua67
-rw-r--r--server/resty/openssl/err.lua62
-rw-r--r--server/resty/openssl/hmac.lua90
-rw-r--r--server/resty/openssl/include/asn1.lua94
-rw-r--r--server/resty/openssl/include/bio.lua13
-rw-r--r--server/resty/openssl/include/bn.lua77
-rw-r--r--server/resty/openssl/include/conf.lua9
-rw-r--r--server/resty/openssl/include/crypto.lua31
-rw-r--r--server/resty/openssl/include/dh.lua80
-rw-r--r--server/resty/openssl/include/ec.lua59
-rw-r--r--server/resty/openssl/include/err.lua9
-rw-r--r--server/resty/openssl/include/evp.lua109
-rw-r--r--server/resty/openssl/include/evp/cipher.lua123
-rw-r--r--server/resty/openssl/include/evp/kdf.lua148
-rw-r--r--server/resty/openssl/include/evp/mac.lua38
-rw-r--r--server/resty/openssl/include/evp/md.lua86
-rw-r--r--server/resty/openssl/include/evp/pkey.lua234
-rw-r--r--server/resty/openssl/include/hmac.lua48
-rw-r--r--server/resty/openssl/include/objects.lua19
-rw-r--r--server/resty/openssl/include/ossl_typ.lua71
-rw-r--r--server/resty/openssl/include/param.lua71
-rw-r--r--server/resty/openssl/include/pem.lua50
-rw-r--r--server/resty/openssl/include/pkcs12.lua31
-rw-r--r--server/resty/openssl/include/provider.lua27
-rw-r--r--server/resty/openssl/include/rand.lua24
-rw-r--r--server/resty/openssl/include/rsa.lua70
-rw-r--r--server/resty/openssl/include/ssl.lua113
-rw-r--r--server/resty/openssl/include/stack.lua95
-rw-r--r--server/resty/openssl/include/x509/altname.lua49
-rw-r--r--server/resty/openssl/include/x509/crl.lua86
-rw-r--r--server/resty/openssl/include/x509/csr.lua88
-rw-r--r--server/resty/openssl/include/x509/extension.lua44
-rw-r--r--server/resty/openssl/include/x509/init.lua138
-rw-r--r--server/resty/openssl/include/x509/name.lua21
-rw-r--r--server/resty/openssl/include/x509/revoked.lua17
-rw-r--r--server/resty/openssl/include/x509_vfy.lua108
-rw-r--r--server/resty/openssl/include/x509v3.lua108
-rw-r--r--server/resty/openssl/kdf.lua388
-rw-r--r--server/resty/openssl/mac.lua96
-rw-r--r--server/resty/openssl/objects.lua74
-rw-r--r--server/resty/openssl/param.lua322
-rw-r--r--server/resty/openssl/pkcs12.lua168
-rw-r--r--server/resty/openssl/pkey.lua942
-rw-r--r--server/resty/openssl/provider.lua136
-rw-r--r--server/resty/openssl/rand.lua51
-rw-r--r--server/resty/openssl/rsa.lua155
-rw-r--r--server/resty/openssl/ssl.lua353
-rw-r--r--server/resty/openssl/ssl_ctx.lua95
-rw-r--r--server/resty/openssl/stack.lua159
-rw-r--r--server/resty/openssl/version.lua117
-rw-r--r--server/resty/openssl/x509/altname.lua248
-rw-r--r--server/resty/openssl/x509/chain.lua76
-rw-r--r--server/resty/openssl/x509/crl.lua607
-rw-r--r--server/resty/openssl/x509/csr.lua531
-rw-r--r--server/resty/openssl/x509/extension.lua281
-rw-r--r--server/resty/openssl/x509/extension/dist_points.lua75
-rw-r--r--server/resty/openssl/x509/extension/info_access.lua137
-rw-r--r--server/resty/openssl/x509/extensions.lua84
-rw-r--r--server/resty/openssl/x509/init.lua1071
-rw-r--r--server/resty/openssl/x509/name.lua156
-rw-r--r--server/resty/openssl/x509/revoked.lua108
-rw-r--r--server/resty/openssl/x509/store.lua227
74 files changed, 11397 insertions, 0 deletions
diff --git a/server/resty/openssl/asn1.lua b/server/resty/openssl/asn1.lua
new file mode 100644
index 0000000..0fa0605
--- /dev/null
+++ b/server/resty/openssl/asn1.lua
@@ -0,0 +1,91 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+local floor = math.floor
+
+local asn1_macro = require("resty.openssl.include.asn1")
+
+-- https://github.com/wahern/luaossl/blob/master/src/openssl.c
+local function isleap(year)
+ return (year % 4) == 0 and ((year % 100) > 0 or (year % 400) == 0)
+end
+
+local past = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }
+local function yday(year, mon, mday)
+ local d = past[mon] + mday - 1
+ if mon > 2 and isleap(year) then
+ d = d + 1
+ end
+ return d
+end
+
+local function leaps(year)
+ return floor(year / 400) + floor(year / 4) - floor(year / 100)
+end
+
+local function asn1_to_unix(asn1)
+ if asn1 == nil then
+ return nil, "except an ASN1 instance at #1, got nil"
+ end
+
+ local s = asn1_macro.ASN1_STRING_get0_data(asn1)
+ s = ffi_str(s)
+ -- V_ASN1_UTCTIME 190303223958Z
+ -- V_ASN1_GENERALIZEDTIME 21190822162753Z
+ local yyoffset = 2
+ local year
+ -- # define V_ASN1_GENERALIZEDTIME 24
+ if C.ASN1_STRING_type(asn1) == 24 then
+ yyoffset = 4
+ year = tonumber(s:sub(1, yyoffset))
+ else
+ year = tonumber(s:sub(1, yyoffset))
+ year = year + (year < 50 and 2000 or 1900)
+ end
+ local month = tonumber(s:sub(yyoffset+1, yyoffset+2))
+ if month > 12 or month < 1 then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+ local day = tonumber(s:sub(yyoffset+3, yyoffset+4))
+ if day > 31 or day < 1 then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+ local hour = tonumber(s:sub(yyoffset+5, yyoffset+6))
+ if hour > 23 or hour < 0 then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+ local minute = tonumber(s:sub(yyoffset+7, yyoffset+8))
+ if minute > 59 or hour < 0 then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+ local second = tonumber(s:sub(yyoffset+9, yyoffset+10))
+ if second > 59 or second < 0 then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+
+ local tm
+ tm = (year - 1970) * 365
+ tm = tm + leaps(year - 1) - leaps(1969)
+ tm = (tm + yday(year, month, day)) * 24
+ tm = (tm + hour) * 60
+ tm = (tm + minute) * 60
+ tm = tm + second
+
+ -- offset?
+ local sign = s:sub(yyoffset+11, yyoffset+11)
+ if sign == "+" or sign == "-" then
+ local sgn = sign == "+" and 1 or -1
+ local hh = tonumber(s:sub(yyoffset+12, yyoffset+13) or 'no')
+ local mm = tonumber(s:sub(yyoffset+14, yyoffset+15) or 'no')
+ if not hh or not mm then
+ return nil, "asn1.asn1_to_unix: bad format " .. s
+ end
+ tm = tm + sgn * (hh * 3600 + mm * 60)
+ end
+
+ return tm
+end
+
+return {
+ asn1_to_unix = asn1_to_unix,
+}
diff --git a/server/resty/openssl/auxiliary/bio.lua b/server/resty/openssl/auxiliary/bio.lua
new file mode 100644
index 0000000..3eed9f0
--- /dev/null
+++ b/server/resty/openssl/auxiliary/bio.lua
@@ -0,0 +1,43 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_new = ffi.new
+local ffi_str = ffi.string
+
+require "resty.openssl.include.bio"
+local format_error = require("resty.openssl.err").format_error
+
+local function read_wrap(f, ...)
+ if type(f) ~= "cdata" then -- should be explictly a function
+ return nil, "bio_util.read_wrap: expect a function at #1"
+ end
+
+ local bio_method = C.BIO_s_mem()
+ if bio_method == nil then
+ return nil, "bio_util.read_wrap: BIO_s_mem() failed"
+ end
+ local bio = C.BIO_new(bio_method)
+ ffi_gc(bio, C.BIO_free)
+
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "bio_util.read_wrap: BIO_ctrl() failed: " .. code
+ end
+
+ local code = f(bio, ...)
+ if code ~= 1 then
+ return nil, format_error(f, code)
+ end
+
+ local buf = ffi_new("char *[1]")
+
+ -- BIO_get_mem_data; #define BIO_CTRL_INFO 3
+ local length = C.BIO_ctrl(bio, 3, 0, buf)
+
+ return ffi_str(buf[0], length)
+end
+
+return {
+ read_wrap = read_wrap,
+} \ No newline at end of file
diff --git a/server/resty/openssl/auxiliary/ctypes.lua b/server/resty/openssl/auxiliary/ctypes.lua
new file mode 100644
index 0000000..933822b
--- /dev/null
+++ b/server/resty/openssl/auxiliary/ctypes.lua
@@ -0,0 +1,28 @@
+-- Put common type definition at the same place for convenience
+-- and standarlization
+local ffi = require "ffi"
+
+--[[
+ TYPE_ptr: usually used to define a pointer (to cast or something)
+ char* var_name; // <- we use char_ptr
+
+ ptr_of_TYPE: usually used to pass the pointer of an object that
+ is already allocated. so that we can also set value of it as well
+
+ int p = 2; // ptr_of_int(); ptr_of_int[0] = 2;
+ plus_one(&p); // <- we use ptr_of_int
+]]
+
+return {
+ void_ptr = ffi.typeof("void *"),
+ ptr_of_uint64 = ffi.typeof("uint64_t[1]"),
+ ptr_of_uint = ffi.typeof("unsigned int[1]"),
+ ptr_of_size_t = ffi.typeof("size_t[1]"),
+ ptr_of_int = ffi.typeof("int[1]"),
+ null = ffi.new("void *"), -- hack wher ngx.null is not available
+
+ uchar_array = ffi.typeof("unsigned char[?]"),
+ uchar_ptr = ffi.typeof("unsigned char*"),
+
+ SIZE_MAX = math.pow(2, 64), -- nginx set _FILE_OFFSET_BITS to 64
+} \ No newline at end of file
diff --git a/server/resty/openssl/auxiliary/jwk.lua b/server/resty/openssl/auxiliary/jwk.lua
new file mode 100644
index 0000000..5a505a9
--- /dev/null
+++ b/server/resty/openssl/auxiliary/jwk.lua
@@ -0,0 +1,261 @@
+
+local ffi = require "ffi"
+local C = ffi.C
+
+local cjson = require("cjson.safe")
+local b64 = require("ngx.base64")
+
+local evp_macro = require "resty.openssl.include.evp"
+local rsa_lib = require "resty.openssl.rsa"
+local ec_lib = require "resty.openssl.ec"
+local ecx_lib = require "resty.openssl.ecx"
+local bn_lib = require "resty.openssl.bn"
+local digest_lib = require "resty.openssl.digest"
+
+local _M = {}
+
+local rsa_jwk_params = {"n", "e", "d", "p", "q", "dp", "dq", "qi"}
+local rsa_openssl_params = rsa_lib.params
+
+local function load_jwk_rsa(tbl)
+ if not tbl["n"] or not tbl["e"] then
+ return nil, "at least \"n\" and \"e\" parameter is required"
+ end
+
+ local params = {}
+ local err
+ for i, k in ipairs(rsa_jwk_params) do
+ local v = tbl[k]
+ if v then
+ v = b64.decode_base64url(v)
+ if not v then
+ return nil, "cannot decode parameter \"" .. k .. "\" from base64 " .. tbl[k]
+ end
+
+ params[rsa_openssl_params[i]], err = bn_lib.from_binary(v)
+ if err then
+ return nil, "cannot use parameter \"" .. k .. "\": " .. err
+ end
+ end
+ end
+
+ local key = C.RSA_new()
+ if key == nil then
+ return nil, "RSA_new() failed"
+ end
+
+ local _, err = rsa_lib.set_parameters(key, params)
+ if err ~= nil then
+ C.RSA_free(key)
+ return nil, err
+ end
+
+ return key
+end
+
+local ec_curves = {
+ ["P-256"] = C.OBJ_ln2nid("prime256v1"),
+ ["P-384"] = C.OBJ_ln2nid("secp384r1"),
+ ["P-521"] = C.OBJ_ln2nid("secp521r1"),
+}
+
+local ec_curves_reverse = {}
+for k, v in pairs(ec_curves) do
+ ec_curves_reverse[v] = k
+end
+
+local ec_jwk_params = {"x", "y", "d"}
+
+local function load_jwk_ec(tbl)
+ local curve = tbl['crv']
+ if not curve then
+ return nil, "\"crv\" not defined for EC key"
+ end
+ if not tbl["x"] or not tbl["y"] then
+ return nil, "at least \"x\" and \"y\" parameter is required"
+ end
+ local curve_nid = ec_curves[curve]
+ if not curve_nid then
+ return nil, "curve \"" .. curve .. "\" is not supported by this library"
+ elseif curve_nid == 0 then
+ return nil, "curve \"" .. curve .. "\" is not supported by linked OpenSSL"
+ end
+
+ local params = {}
+ local err
+ for _, k in ipairs(ec_jwk_params) do
+ local v = tbl[k]
+ if v then
+ v = b64.decode_base64url(v)
+ if not v then
+ return nil, "cannot decode parameter \"" .. k .. "\" from base64 " .. tbl[k]
+ end
+
+ params[k], err = bn_lib.from_binary(v)
+ if err then
+ return nil, "cannot use parameter \"" .. k .. "\": " .. err
+ end
+ end
+ end
+
+ -- map to the name we expect
+ if params["d"] then
+ params["private"] = params["d"]
+ params["d"] = nil
+ end
+ params["group"] = curve_nid
+
+ local key = C.EC_KEY_new()
+ if key == nil then
+ return nil, "EC_KEY_new() failed"
+ end
+
+ local _, err = ec_lib.set_parameters(key, params)
+ if err ~= nil then
+ C.EC_KEY_free(key)
+ return nil, err
+ end
+
+ return key
+end
+
+local function load_jwk_okp(key_type, tbl)
+ local params = {}
+ if tbl["d"] then
+ params.private = b64.decode_base64url(tbl["d"])
+ elseif tbl["x"] then
+ params.public = b64.decode_base64url(tbl["x"])
+ else
+ return nil, "at least \"x\" or \"d\" parameter is required"
+ end
+ local key, err = ecx_lib.set_parameters(key_type, nil, params)
+ if err ~= nil then
+ return nil, err
+ end
+ return key
+end
+
+local ecx_curves_reverse = {}
+for k, v in pairs(evp_macro.ecx_curves) do
+ ecx_curves_reverse[v] = k
+end
+
+function _M.load_jwk(txt)
+ local tbl, err = cjson.decode(txt)
+ if err then
+ return nil, "error decoding JSON from JWK: " .. err
+ elseif type(tbl) ~= "table" then
+ return nil, "except input to be decoded as a table, got " .. type(tbl)
+ end
+
+ local key, key_free, key_type, err
+
+ if tbl["kty"] == "RSA" then
+ key_type = evp_macro.EVP_PKEY_RSA
+ if key_type == 0 then
+ return nil, "the linked OpenSSL library doesn't support RSA key"
+ end
+ key, err = load_jwk_rsa(tbl)
+ key_free = C.RSA_free
+ elseif tbl["kty"] == "EC" then
+ key_type = evp_macro.EVP_PKEY_EC
+ if key_type == 0 then
+ return nil, "the linked OpenSSL library doesn't support EC key"
+ end
+ key, err = load_jwk_ec(tbl)
+ key_free = C.EC_KEY_free
+ elseif tbl["kty"] == "OKP" then
+ local curve = tbl["crv"]
+ key_type = evp_macro.ecx_curves[curve]
+ if not key_type then
+ return nil, "unknown curve \"" .. tostring(curve)
+ elseif key_type == 0 then
+ return nil, "the linked OpenSSL library doesn't support \"" .. curve .. "\" key"
+ end
+ key, err = load_jwk_okp(key_type, tbl)
+ if key ~= nil then
+ return key
+ end
+ else
+ return nil, "not yet supported jwk type \"" .. (tbl["kty"] or "nil") .. "\""
+ end
+
+ if err then
+ return nil, "failed to construct " .. tbl["kty"] .. " key from JWK: " .. err
+ end
+
+ local ctx = C.EVP_PKEY_new()
+ if ctx == nil then
+ key_free(key)
+ return nil, "EVP_PKEY_new() failed"
+ end
+
+ local code = C.EVP_PKEY_assign(ctx, key_type, key)
+ if code ~= 1 then
+ key_free(key)
+ C.EVP_PKEY_free(ctx)
+ return nil, "EVP_PKEY_assign() failed"
+ end
+
+ return ctx
+end
+
+function _M.dump_jwk(pkey, is_priv)
+ local jwk
+ if pkey.key_type == evp_macro.EVP_PKEY_RSA then
+ local param_keys = { "n" , "e" }
+ if is_priv then
+ param_keys = rsa_jwk_params
+ end
+ local params, err = pkey:get_parameters()
+ if err then
+ return nil, "jwk.dump_jwk: " .. err
+ end
+ jwk = {
+ kty = "RSA",
+ }
+ for i, p in ipairs(param_keys) do
+ local v = params[rsa_openssl_params[i]]:to_binary()
+ jwk[p] = b64.encode_base64url(v)
+ end
+ elseif pkey.key_type == evp_macro.EVP_PKEY_EC then
+ local params, err = pkey:get_parameters()
+ if err then
+ return nil, "jwk.dump_jwk: " .. err
+ end
+ jwk = {
+ kty = "EC",
+ crv = ec_curves_reverse[params.group],
+ x = b64.encode_base64url(params.x:to_binary()),
+ y = b64.encode_base64url(params.x:to_binary()),
+ }
+ if is_priv then
+ jwk.d = b64.encode_base64url(params.private:to_binary())
+ end
+ elseif ecx_curves_reverse[pkey.key_type] then
+ local params, err = pkey:get_parameters()
+ if err then
+ return nil, "jwk.dump_jwk: " .. err
+ end
+ jwk = {
+ kty = "OKP",
+ crv = ecx_curves_reverse[pkey.key_type],
+ d = b64.encode_base64url(params.private),
+ x = b64.encode_base64url(params.public),
+ }
+ else
+ return nil, "jwk.dump_jwk: not implemented for this key type"
+ end
+
+ local der = pkey:tostring(is_priv and "private" or "public", "DER")
+ local dgst = digest_lib.new("sha256")
+ local d, err = dgst:final(der)
+ if err then
+ return nil, "jwk.dump_jwk: failed to calculate digest for key"
+ end
+ jwk.kid = b64.encode_base64url(d)
+
+ return cjson.encode(jwk)
+end
+
+return _M
diff --git a/server/resty/openssl/auxiliary/nginx.lua b/server/resty/openssl/auxiliary/nginx.lua
new file mode 100644
index 0000000..8adeceb
--- /dev/null
+++ b/server/resty/openssl/auxiliary/nginx.lua
@@ -0,0 +1,318 @@
+local get_req_ssl, get_req_ssl_ctx
+local get_socket_ssl, get_socket_ssl_ctx
+
+local pok, nginx_c = pcall(require, "resty.openssl.auxiliary.nginx_c")
+
+if pok and not os.getenv("CI_SKIP_NGINX_C") then
+ get_req_ssl = nginx_c.get_req_ssl
+ get_req_ssl_ctx = nginx_c.get_req_ssl_ctx
+ get_socket_ssl = nginx_c.get_socket_ssl
+ get_socket_ssl_ctx = nginx_c.get_socket_ssl
+else
+ local ffi = require "ffi"
+
+ ffi.cdef [[
+ // Nginx seems to always config _FILE_OFFSET_BITS=64, this should always be 8 byte
+ typedef long long off_t;
+ typedef unsigned int socklen_t; // windows uses int, same size
+ typedef unsigned short in_port_t;
+
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
+
+ typedef long (*ngx_recv_pt)(void *c, void *buf, size_t size);
+ typedef long (*ngx_recv_chain_pt)(void *c, void *in,
+ off_t limit);
+ typedef long (*ngx_send_pt)(void *c, void *buf, size_t size);
+ typedef void *(*ngx_send_chain_pt)(void *c, void *in,
+ off_t limit);
+
+ typedef struct {
+ size_t len;
+ void *data;
+ } ngx_str_t;
+
+ typedef struct {
+ SSL *connection;
+ SSL_CTX *session_ctx;
+ // trimmed
+ } ngx_ssl_connection_s;
+ ]]
+
+ local ngx_version = ngx.config.nginx_version
+ if ngx_version == 1017008 or ngx_version == 1019003 or ngx_version == 1019009
+ or ngx_version == 1021004 then
+ -- 1.17.8, 1.19.3, 1.19.9, 1.21.4
+ -- https://github.com/nginx/nginx/blob/master/src/core/ngx_connection.h
+ ffi.cdef [[
+ typedef struct {
+ ngx_str_t src_addr;
+ ngx_str_t dst_addr;
+ in_port_t src_port;
+ in_port_t dst_port;
+ } ngx_proxy_protocol_t;
+
+ typedef struct {
+ void *data;
+ void *read;
+ void *write;
+
+ int fd;
+
+ ngx_recv_pt recv;
+ ngx_send_pt send;
+ ngx_recv_chain_pt recv_chain;
+ ngx_send_chain_pt send_chain;
+
+ void *listening;
+
+ off_t sent;
+
+ void *log;
+
+ void *pool;
+
+ int type;
+
+ void *sockaddr;
+ socklen_t socklen;
+ ngx_str_t addr_text;
+
+ // https://github.com/nginx/nginx/commit/be932e81a1531a3ba032febad968fc2006c4fa48
+ ngx_proxy_protocol_t *proxy_protocol;
+
+ ngx_ssl_connection_s *ssl;
+ // trimmed
+ } ngx_connection_s;
+ ]]
+ else
+ error("resty.openssl.auxiliary.nginx doesn't support Nginx version " .. ngx_version, 2)
+ end
+
+ ffi.cdef [[
+ typedef struct {
+ ngx_connection_s *connection;
+ // trimmed
+ } ngx_stream_lua_request_s;
+
+ typedef struct {
+ unsigned int signature; /* "HTTP" */
+
+ ngx_connection_s *connection;
+ // trimmed
+ } ngx_http_request_s;
+ ]]
+
+ local get_request
+ do
+ local ok, exdata = pcall(require, "thread.exdata")
+ if ok and exdata then
+ function get_request()
+ local r = exdata()
+ if r ~= nil then
+ return r
+ end
+ end
+
+ else
+ local getfenv = getfenv
+
+ function get_request()
+ return getfenv(0).__ngx_req
+ end
+ end
+ end
+
+ local SOCKET_CTX_INDEX = 1
+
+ local NO_C_MODULE_WARNING_MSG_SHOWN = false
+ local NO_C_MODULE_WARNING_MSG = "note resty.openssl.auxiliary.nginx is using plain FFI " ..
+ "and it's only intended to be used in development, " ..
+ "consider using lua-resty-openssl.aux-module in production."
+
+ local function get_ngx_ssl_from_req()
+ if not NO_C_MODULE_WARNING_MSG_SHOWN then
+ ngx.log(ngx.WARN, NO_C_MODULE_WARNING_MSG)
+ NO_C_MODULE_WARNING_MSG_SHOWN = true
+ end
+
+ local c = get_request()
+ if ngx.config.subsystem == "stream" then
+ c = ffi.cast("ngx_stream_lua_request_s*", c)
+ else -- http
+ c = ffi.cast("ngx_http_request_s*", c)
+ end
+
+ local ngx_ssl = c.connection.ssl
+ if ngx_ssl == nil then
+ return nil, "c.connection.ssl is nil"
+ end
+ return ngx_ssl
+ end
+
+ get_req_ssl = function()
+ local ssl, err = get_ngx_ssl_from_req()
+ if err then
+ return nil, err
+ end
+
+ return ssl.connection
+ end
+
+ get_req_ssl_ctx = function()
+ local ssl, err = get_ngx_ssl_from_req()
+ if err then
+ return nil, err
+ end
+
+ return ssl.session_ctx
+ end
+
+ ffi.cdef[[
+ typedef struct ngx_http_lua_socket_tcp_upstream_s
+ ngx_http_lua_socket_tcp_upstream_t;
+
+ typedef struct {
+ ngx_connection_s *connection;
+ // trimmed
+ } ngx_peer_connection_s;
+
+ typedef
+ int (*ngx_http_lua_socket_tcp_retval_handler_masked)(void *r,
+ void *u, void *L);
+
+ typedef void (*ngx_http_lua_socket_tcp_upstream_handler_pt_masked)
+ (void *r, void *u);
+
+
+ typedef
+ int (*ngx_stream_lua_socket_tcp_retval_handler)(void *r,
+ void *u, void *L);
+
+ typedef void (*ngx_stream_lua_socket_tcp_upstream_handler_pt)
+ (void *r, void *u);
+
+ typedef struct {
+ ngx_stream_lua_socket_tcp_retval_handler read_prepare_retvals;
+ ngx_stream_lua_socket_tcp_retval_handler write_prepare_retvals;
+ ngx_stream_lua_socket_tcp_upstream_handler_pt read_event_handler;
+ ngx_stream_lua_socket_tcp_upstream_handler_pt write_event_handler;
+
+ void *socket_pool;
+
+ void *conf;
+ void *cleanup;
+ void *request;
+
+ ngx_peer_connection_s peer;
+ // trimmed
+ } ngx_stream_lua_socket_tcp_upstream_s;
+ ]]
+
+ local ngx_lua_version = ngx.config and
+ ngx.config.ngx_lua_version and
+ ngx.config.ngx_lua_version
+
+ if ngx_lua_version >= 10019 and ngx_lua_version <= 10021 then
+ -- https://github.com/openresty/lua-nginx-module/blob/master/src/ngx_http_lua_socket_tcp.h
+ ffi.cdef[[
+ typedef struct {
+ ngx_http_lua_socket_tcp_retval_handler_masked read_prepare_retvals;
+ ngx_http_lua_socket_tcp_retval_handler_masked write_prepare_retvals;
+ ngx_http_lua_socket_tcp_upstream_handler_pt_masked read_event_handler;
+ ngx_http_lua_socket_tcp_upstream_handler_pt_masked write_event_handler;
+
+ void *udata_queue; // 0.10.19
+
+ void *socket_pool;
+
+ void *conf;
+ void *cleanup;
+ void *request;
+ ngx_peer_connection_s peer;
+ // trimmed
+ } ngx_http_lua_socket_tcp_upstream_s;
+ ]]
+ elseif ngx_lua_version < 10019 then
+ -- the struct doesn't seem to get changed a long time since birth
+ ffi.cdef[[
+ typedef struct {
+ ngx_http_lua_socket_tcp_retval_handler_masked read_prepare_retvals;
+ ngx_http_lua_socket_tcp_retval_handler_masked write_prepare_retvals;
+ ngx_http_lua_socket_tcp_upstream_handler_pt_masked read_event_handler;
+ ngx_http_lua_socket_tcp_upstream_handler_pt_masked write_event_handler;
+
+ void *socket_pool;
+
+ void *conf;
+ void *cleanup;
+ void *request;
+ ngx_peer_connection_s peer;
+ // trimmed
+ } ngx_http_lua_socket_tcp_upstream_s;
+ ]]
+ else
+ error("resty.openssl.auxiliary.nginx doesn't support lua-nginx-module version " .. (ngx_lua_version or "nil"), 2)
+ end
+
+ local function get_ngx_ssl_from_socket_ctx(sock)
+ if not NO_C_MODULE_WARNING_MSG_SHOWN then
+ ngx.log(ngx.WARN, NO_C_MODULE_WARNING_MSG)
+ NO_C_MODULE_WARNING_MSG_SHOWN = true
+ end
+
+ local u = sock[SOCKET_CTX_INDEX]
+ if u == nil then
+ return nil, "lua_socket_tcp_upstream_t not found"
+ end
+
+ if ngx.config.subsystem == "stream" then
+ u = ffi.cast("ngx_stream_lua_socket_tcp_upstream_s*", u)
+ else -- http
+ u = ffi.cast("ngx_http_lua_socket_tcp_upstream_s*", u)
+ end
+
+ local p = u.peer
+ if p == nil then
+ return nil, "u.peer is nil"
+ end
+
+ local uc = p.connection
+ if uc == nil then
+ return nil, "u.peer.connection is nil"
+ end
+
+ local ngx_ssl = uc.ssl
+ if ngx_ssl == nil then
+ return nil, "u.peer.connection.ssl is nil"
+ end
+ return ngx_ssl
+ end
+
+ get_socket_ssl = function(sock)
+ local ssl, err = get_ngx_ssl_from_socket_ctx(sock)
+ if err then
+ return nil, err
+ end
+
+ return ssl.connection
+ end
+
+ get_socket_ssl_ctx = function(sock)
+ local ssl, err = get_ngx_ssl_from_socket_ctx(sock)
+ if err then
+ return nil, err
+ end
+
+ return ssl.session_ctx
+ end
+
+end
+
+
+return {
+ get_req_ssl = get_req_ssl,
+ get_req_ssl_ctx = get_req_ssl_ctx,
+ get_socket_ssl = get_socket_ssl,
+ get_socket_ssl_ctx = get_socket_ssl_ctx,
+}
diff --git a/server/resty/openssl/auxiliary/nginx_c.lua b/server/resty/openssl/auxiliary/nginx_c.lua
new file mode 100644
index 0000000..f50db36
--- /dev/null
+++ b/server/resty/openssl/auxiliary/nginx_c.lua
@@ -0,0 +1,154 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+local SOCKET_CTX_INDEX = 1
+local NGX_OK = ngx.OK
+
+
+local get_req_ssl, get_req_ssl_ctx
+local get_socket_ssl, get_socket_ssl_ctx
+
+local get_request
+do
+ local ok, exdata = pcall(require, "thread.exdata")
+ if ok and exdata then
+ function get_request()
+ local r = exdata()
+ if r ~= nil then
+ return r
+ end
+ end
+
+ else
+ local getfenv = getfenv
+
+ function get_request()
+ return getfenv(0).__ngx_req
+ end
+ end
+end
+
+
+local stream_subsystem = false
+if ngx.config.subsystem == "stream" then
+ stream_subsystem = true
+
+ ffi.cdef [[
+ typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
+ typedef struct ngx_stream_lua_socket_tcp_upstream_s ngx_stream_lua_socket_tcp_upstream_t;
+
+ int ngx_stream_lua_resty_openssl_aux_get_request_ssl(ngx_stream_lua_request_t *r,
+ void **_ssl_conn);
+
+ int ngx_stream_lua_resty_openssl_aux_get_request_ssl_ctx(ngx_stream_lua_request_t *r,
+ void **_sess);
+
+ int ngx_stream_lua_resty_openssl_aux_get_socket_ssl(ngx_stream_lua_socket_tcp_upstream_t *u,
+ void **_ssl_conn);
+
+ int ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_stream_lua_socket_tcp_upstream_t *u,
+ void **_sess);
+ ]]
+
+ -- sanity test
+ local _ = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl
+else
+ ffi.cdef [[
+ typedef struct ngx_http_request_s ngx_http_request_t;
+ typedef struct ngx_http_lua_socket_tcp_upstream_s ngx_http_lua_socket_tcp_upstream_t;
+
+ int ngx_http_lua_resty_openssl_aux_get_request_ssl(ngx_http_request_t *r,
+ void **_ssl_conn);
+
+ int ngx_http_lua_resty_openssl_aux_get_request_ssl_ctx(ngx_http_request_t *r,
+ void **_sess);
+
+ int ngx_http_lua_resty_openssl_aux_get_socket_ssl(ngx_http_lua_socket_tcp_upstream_t *u,
+ void **_ssl_conn);
+
+ int ngx_http_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_http_lua_socket_tcp_upstream_t *u,
+ void **_sess);
+ ]]
+
+ -- sanity test
+ local _ = C.ngx_http_lua_resty_openssl_aux_get_request_ssl
+end
+
+local void_pp = ffi.new("void *[1]")
+local ssl_type = ffi.typeof("SSL*")
+local ssl_ctx_type = ffi.typeof("SSL_CTX*")
+
+get_req_ssl = function()
+ local c = get_request()
+
+ local ret
+ if stream_subsystem then
+ ret = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl(c, void_pp)
+ else
+ ret = C.ngx_http_lua_resty_openssl_aux_get_request_ssl(c, void_pp)
+ end
+
+ if ret ~= NGX_OK then
+ return nil, "cannot read r->connection->ssl->connection"
+ end
+
+ return ffi.cast(ssl_type, void_pp[0])
+end
+
+get_req_ssl_ctx = function()
+ local c = get_request()
+
+ local ret
+ if stream_subsystem then
+ ret = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl_ctx(c, void_pp)
+ else
+ ret = C.ngx_http_lua_resty_openssl_aux_get_request_ssl_ctx(c, void_pp)
+ end
+
+ if ret ~= NGX_OK then
+ return nil, "cannot read r->connection->ssl->session_ctx"
+ end
+
+ return ffi.cast(ssl_ctx_type, void_pp[0])
+end
+
+get_socket_ssl = function(sock)
+ local u = sock[SOCKET_CTX_INDEX]
+
+ local ret
+ if stream_subsystem then
+ ret = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
+ else
+ ret = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
+ end
+
+ if ret ~= NGX_OK then
+ return nil, "cannot read u->peer.connection->ssl->connection"
+ end
+
+ return ffi.cast(ssl_type, void_pp[0])
+end
+
+get_socket_ssl_ctx = function(sock)
+ local u = sock[SOCKET_CTX_INDEX]
+
+ local ret
+ if stream_subsystem then
+ ret = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx(u, void_pp)
+ else
+ ret = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl_ctx(u, void_pp)
+ end
+
+ if ret ~= NGX_OK then
+ return nil, "cannot read u->peer.connection->ssl->session_ctx"
+ end
+
+ return ffi.cast(ssl_ctx_type, void_pp[0])
+end
+
+return {
+ get_req_ssl = get_req_ssl,
+ get_req_ssl_ctx = get_req_ssl_ctx,
+ get_socket_ssl = get_socket_ssl,
+ get_socket_ssl_ctx = get_socket_ssl_ctx,
+} \ No newline at end of file
diff --git a/server/resty/openssl/bn.lua b/server/resty/openssl/bn.lua
new file mode 100644
index 0000000..e893e5e
--- /dev/null
+++ b/server/resty/openssl/bn.lua
@@ -0,0 +1,416 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_new = ffi.new
+local ffi_str = ffi.string
+local floor = math.floor
+
+require "resty.openssl.include.bn"
+local crypto_macro = require("resty.openssl.include.crypto")
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = {__index = _M}
+
+local bn_ptr_ct = ffi.typeof('BIGNUM*')
+local bn_ptrptr_ct = ffi.typeof('BIGNUM*[1]')
+
+function _M.new(bn)
+ local ctx = C.BN_new()
+ ffi_gc(ctx, C.BN_free)
+
+ if type(bn) == 'number' then
+ if C.BN_set_word(ctx, bn) ~= 1 then
+ return nil, format_error("bn.new")
+ end
+ elseif bn then
+ return nil, "bn.new: expect nil or a number at #1"
+ end
+
+ return setmetatable( { ctx = ctx }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(bn_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(bn_ptr_ct, ctx) then
+ return nil, "bn.dup: expect a bn ctx at #1"
+ end
+ local ctx = C.BN_dup(ctx)
+ ffi_gc(ctx, C.BN_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self
+end
+
+function _M:to_binary()
+ local length = (C.BN_num_bits(self.ctx)+7)/8
+ -- align to bytes
+ length = floor(length)
+ local buf = ctypes.uchar_array(length)
+ local sz = C.BN_bn2bin(self.ctx, buf)
+ if sz == 0 then
+ return nil, format_error("bn:to_binary")
+ end
+ buf = ffi_str(buf, length)
+ return buf
+end
+
+function _M.from_binary(s)
+ if type(s) ~= "string" then
+ return nil, "bn.from_binary: expect a string at #1"
+ end
+
+ local ctx = C.BN_bin2bn(s, #s, nil)
+ if ctx == nil then
+ return nil, format_error("bn.from_binary")
+ end
+ ffi_gc(ctx, C.BN_free)
+ return setmetatable( { ctx = ctx }, mt), nil
+end
+
+function _M:to_hex()
+ local buf = C.BN_bn2hex(self.ctx)
+ if buf == nil then
+ return nil, format_error("bn:to_hex")
+ end
+ ffi_gc(buf, crypto_macro.OPENSSL_free)
+ local s = ffi_str(buf)
+ return s
+end
+
+function _M.from_hex(s)
+ if type(s) ~= "string" then
+ return nil, "bn.from_hex: expect a string at #1"
+ end
+
+ local p = ffi_new(bn_ptrptr_ct)
+
+ if C.BN_hex2bn(p, s) == 0 then
+ return nil, format_error("bn.from_hex")
+ end
+ local ctx = p[0]
+ ffi_gc(ctx, C.BN_free)
+ return setmetatable( { ctx = ctx }, mt), nil
+end
+
+function _M:to_dec()
+ local buf = C.BN_bn2dec(self.ctx)
+ if buf == nil then
+ return nil, format_error("bn:to_dec")
+ end
+ ffi_gc(buf, crypto_macro.OPENSSL_free)
+ local s = ffi_str(buf)
+ return s
+end
+mt.__tostring = _M.to_dec
+
+function _M.from_dec(s)
+ if type(s) ~= "string" then
+ return nil, "bn.from_dec: expect a string at #1"
+ end
+
+ local p = ffi_new(bn_ptrptr_ct)
+
+ if C.BN_dec2bn(p, s) == 0 then
+ return nil, format_error("bn.from_dec")
+ end
+ local ctx = p[0]
+ ffi_gc(ctx, C.BN_free)
+ return setmetatable( { ctx = ctx }, mt), nil
+end
+
+function _M:to_number()
+ return tonumber(C.BN_get_word(self.ctx))
+end
+_M.tonumber = _M.to_number
+
+function _M.generate_prime(bits, safe)
+ local ctx = C.BN_new()
+ ffi_gc(ctx, C.BN_free)
+
+ if C.BN_generate_prime_ex(ctx, bits, safe and 1 or 0, nil, nil, nil) == 0 then
+ return nil, format_error("bn.BN_generate_prime_ex")
+ end
+
+ return setmetatable( { ctx = ctx }, mt), nil
+end
+
+-- BN_CTX is used to store temporary variable
+-- we only need one per worker
+local bn_ctx_tmp = C.BN_CTX_new()
+assert(bn_ctx_tmp ~= nil)
+if OPENSSL_10 then
+ C.BN_CTX_init(bn_ctx_tmp)
+end
+ffi_gc(bn_ctx_tmp, C.BN_CTX_free)
+
+_M.bn_ctx_tmp = bn_ctx_tmp
+
+-- mathematics
+
+local is_negative
+if OPENSSL_10 then
+ local bn_zero = assert(_M.new(0)).ctx
+ is_negative = function(ctx)
+ return C.BN_cmp(ctx, bn_zero) < 0 and 1 or 0
+ end
+else
+ is_negative = C.BN_is_negative
+end
+function mt.__unm(a)
+ local b = _M.dup(a.ctx)
+ if b == nil then
+ error("BN_dup() failed")
+ end
+ local sign = is_negative(b.ctx)
+ C.BN_set_negative(b.ctx, 1-sign)
+ return b
+end
+
+local function check_args(op, ...)
+ local args = {...}
+ for i, arg in ipairs(args) do
+ if type(arg) == 'number' then
+ local b = C.BN_new()
+ if b == nil then
+ error("BN_new() failed")
+ end
+ ffi_gc(b, C.BN_free)
+ if C.BN_set_word(b, arg) ~= 1 then
+ error("BN_set_word() failed")
+ end
+ args[i] = b
+ elseif _M.istype(arg) then
+ args[i] = arg.ctx
+ else
+ error("cannot " .. op .. " a " .. type(arg) .. " to bignum")
+ end
+ end
+ local ctx = C.BN_new()
+ if ctx == nil then
+ error("BN_new() failed")
+ end
+ ffi_gc(ctx, C.BN_free)
+ local r = setmetatable( { ctx = ctx }, mt)
+ return r, unpack(args)
+end
+
+
+function mt.__add(...)
+ local r, a, b = check_args("add", ...)
+ if C.BN_add(r.ctx, a, b) == 0 then
+ error("BN_add() failed")
+ end
+ return r
+end
+_M.add = mt.__add
+
+function mt.__sub(...)
+ local r, a, b = check_args("substract", ...)
+ if C.BN_sub(r.ctx, a, b) == 0 then
+ error("BN_sub() failed")
+ end
+ return r
+end
+_M.sub = mt.__sub
+
+function mt.__mul(...)
+ local r, a, b = check_args("multiply", ...)
+ if C.BN_mul(r.ctx, a, b, bn_ctx_tmp) == 0 then
+ error("BN_mul() failed")
+ end
+ return r
+end
+_M.mul = mt.__mul
+
+-- lua 5.3 only
+function mt.__idiv(...)
+ local r, a, b = check_args("divide", ...)
+ if C.BN_div(r.ctx, nil, a, b, bn_ctx_tmp) == 0 then
+ error("BN_div() failed")
+ end
+ return r
+end
+
+mt.__div = mt.__idiv
+_M.idiv = mt.__idiv
+_M.div = mt.__div
+
+function mt.__mod(...)
+ local r, a, b = check_args("mod", ...)
+ if C.BN_div(nil, r.ctx, a, b, bn_ctx_tmp) == 0 then
+ error("BN_div() failed")
+ end
+ return r
+end
+_M.mod = mt.__mod
+
+-- __concat doesn't make sense at all?
+
+function _M.sqr(...)
+ local r, a = check_args("square", ...)
+ if C.BN_sqr(r.ctx, a, bn_ctx_tmp) == 0 then
+ error("BN_sqr() failed")
+ end
+ return r
+end
+
+function _M.gcd(...)
+ local r, a, b = check_args("extract greatest common divisor", ...)
+ if C.BN_gcd(r.ctx, a, b, bn_ctx_tmp) == 0 then
+ error("BN_gcd() failed")
+ end
+ return r
+end
+
+function _M.exp(...)
+ local r, a, b = check_args("power", ...)
+ if C.BN_exp(r.ctx, a, b, bn_ctx_tmp) == 0 then
+ error("BN_exp() failed")
+ end
+ return r
+end
+_M.pow = _M.exp
+
+for _, op in ipairs({ "add", "sub" , "mul", "exp" }) do
+ local f = "BN_mod_" .. op
+ local cf = C[f]
+ _M["mod_" .. op] = function(...)
+ local r, a, b, m = check_args(op, ...)
+ if cf(r.ctx, a, b, m, bn_ctx_tmp) == 0 then
+ error(f .. " failed")
+ end
+ return r
+ end
+end
+
+function _M.mod_sqr(...)
+ local r, a, m = check_args("mod_sub", ...)
+ if C.BN_mod_sqr(r.ctx, a, m, bn_ctx_tmp) == 0 then
+ error("BN_mod_sqr() failed")
+ end
+ return r
+end
+
+local function nyi()
+ error("NYI")
+end
+
+-- bit operations, lua 5.3
+
+mt.__band = nyi
+mt.__bor = nyi
+mt.__bxor = nyi
+mt.__bnot = nyi
+
+function mt.__shl(a, b)
+ local r, a = check_args("lshift", a)
+ if C.BN_lshift(r.ctx, a, b) == 0 then
+ error("BN_lshift() failed")
+ end
+ return r
+end
+_M.lshift = mt.__shl
+
+function mt.__shr(a, b)
+ local r, a = check_args("rshift", a)
+ if C.BN_rshift(r.ctx, a, b) == 0 then
+ error("BN_lshift() failed")
+ end
+ return r
+end
+_M.rshift = mt.__shr
+
+-- comparaions
+-- those functions are only called when the table
+-- has exact same metamethods, i.e. they are all BN
+-- so we don't need to check args
+
+function mt.__eq(a, b)
+ return C.BN_cmp(a.ctx, b.ctx) == 0
+end
+
+function mt.__lt(a, b)
+ return C.BN_cmp(a.ctx, b.ctx) < 0
+end
+
+function mt.__le(a, b)
+ return C.BN_cmp(a.ctx, b.ctx) <= 0
+end
+
+if OPENSSL_10 then
+ -- in openssl 1.0.x those functions are implemented as macros
+ -- don't want to copy paste all structs here
+ -- the followings are definitely slower, but works
+ local bn_zero = assert(_M.new(0)).ctx
+ local bn_one = assert(_M.new(1)).ctx
+
+ function _M:is_zero()
+ return C.BN_cmp(self.ctx, bn_zero) == 0
+ end
+
+ function _M:is_one()
+ return C.BN_cmp(self.ctx, bn_one) == 0
+ end
+
+ function _M:is_word(n)
+ local ctx = C.BN_new()
+ ffi_gc(ctx, C.BN_free)
+ if ctx == nil then
+ return nil, "bn:is_word: BN_new() failed"
+ end
+ if C.BN_set_word(ctx, n) ~= 1 then
+ return nil, "bn:is_word: BN_set_word() failed"
+ end
+ return C.BN_cmp(self.ctx, ctx) == 0
+ end
+
+ function _M:is_odd()
+ return self:to_number() % 2 == 1
+ end
+else
+ function _M:is_zero()
+ return C.BN_is_zero(self.ctx) == 1
+ end
+
+ function _M:is_one()
+ return C.BN_is_one(self.ctx) == 1
+ end
+
+ function _M:is_word(n)
+ return C.BN_is_word(self.ctx, n) == 1
+ end
+
+ function _M:is_odd()
+ return C.BN_is_odd(self.ctx) == 1
+ end
+end
+
+function _M:is_prime(nchecks)
+ if nchecks and type(nchecks) ~= "number" then
+ return nil, "bn:is_prime: expect a number at #1"
+ end
+ -- if nchecks is not defined, set to BN_prime_checks:
+ -- select number of iterations based on the size of the number
+ local code
+ if OPENSSL_3X then
+ code = C.BN_check_prime(self.ctx, bn_ctx_tmp, nil)
+ else
+ code = C.BN_is_prime_ex(self.ctx, nchecks or 0, bn_ctx_tmp, nil)
+ end
+ if code == -1 then
+ return nil, format_error("bn.is_prime")
+ end
+ return code == 1
+end
+
+return _M
diff --git a/server/resty/openssl/cipher.lua b/server/resty/openssl/cipher.lua
new file mode 100644
index 0000000..693ac09
--- /dev/null
+++ b/server/resty/openssl/cipher.lua
@@ -0,0 +1,300 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.evp.cipher"
+local evp_macro = require "resty.openssl.include.evp"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local uchar_array = ctypes.uchar_array
+local void_ptr = ctypes.void_ptr
+local ptr_of_int = ctypes.ptr_of_int
+local uchar_ptr = ctypes.uchar_ptr
+
+local _M = {}
+local mt = {__index = _M}
+
+local cipher_ctx_ptr_ct = ffi.typeof('EVP_CIPHER_CTX*')
+
+local out_length = ptr_of_int()
+-- EVP_MAX_BLOCK_LENGTH is 32, we give it a 64 to be future proof
+local out_buffer = ctypes.uchar_array(1024 + 64)
+
+function _M.new(typ, properties)
+ if not typ then
+ return nil, "cipher.new: expect type to be defined"
+ end
+
+ local ctx
+ if OPENSSL_11_OR_LATER then
+ ctx = C.EVP_CIPHER_CTX_new()
+ ffi_gc(ctx, C.EVP_CIPHER_CTX_free)
+ elseif OPENSSL_10 then
+ ctx = ffi.new('EVP_CIPHER_CTX')
+ C.EVP_CIPHER_CTX_init(ctx)
+ ffi_gc(ctx, C.EVP_CIPHER_CTX_cleanup)
+ end
+ if ctx == nil then
+ return nil, "cipher.new: failed to create EVP_CIPHER_CTX"
+ end
+
+ local ctyp
+ if OPENSSL_3X then
+ ctyp = C.EVP_CIPHER_fetch(ctx_lib.get_libctx(), typ, properties)
+ else
+ ctyp = C.EVP_get_cipherbyname(typ)
+ end
+ local err_new = string.format("cipher.new: invalid cipher type \"%s\"", typ)
+ if ctyp == nil then
+ return nil, format_error(err_new)
+ end
+
+ local code = C.EVP_CipherInit_ex(ctx, ctyp, nil, "", nil, -1)
+ if code ~= 1 then
+ return nil, format_error(err_new)
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ algo = ctyp,
+ initialized = false,
+ block_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_block_size(ctx)
+ or C.EVP_CIPHER_CTX_block_size(ctx)),
+ key_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_key_length(ctx)
+ or C.EVP_CIPHER_CTX_key_length(ctx)),
+ iv_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_iv_length(ctx)
+ or C.EVP_CIPHER_CTX_iv_length(ctx)),
+ }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(cipher_ctx_ptr_ct, l.ctx)
+end
+
+function _M:get_provider_name()
+ if not OPENSSL_3X then
+ return false, "cipher:get_provider_name is not supported"
+ end
+ local p = C.EVP_CIPHER_get0_provider(self.algo)
+ if p == nil then
+ return nil
+ end
+ return ffi_str(C.OSSL_PROVIDER_get0_name(p))
+end
+
+if OPENSSL_3X then
+ local param_lib = require "resty.openssl.param"
+ _M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_CIPHER_CTX")
+end
+
+function _M:init(key, iv, opts)
+ opts = opts or {}
+ if not key or #key ~= self.key_size then
+ return false, string.format("cipher:init: incorrect key size, expect %d", self.key_size)
+ end
+ if not iv or #iv ~= self.iv_size then
+ return false, string.format("cipher:init: incorrect iv size, expect %d", self.iv_size)
+ end
+
+ -- always passed in the `EVP_CIPHER` parameter to reinitialized the cipher
+ -- it will have a same effect as EVP_CIPHER_CTX_cleanup/EVP_CIPHER_CTX_reset then Init_ex with
+ -- empty algo
+ if C.EVP_CipherInit_ex(self.ctx, self.algo, nil, key, iv, opts.is_encrypt and 1 or 0) == 0 then
+ return false, format_error("cipher:init EVP_CipherInit_ex")
+ end
+
+ if opts.no_padding then
+ -- EVP_CIPHER_CTX_set_padding() always returns 1.
+ C.EVP_CIPHER_CTX_set_padding(self.ctx, 0)
+ end
+
+ self.initialized = true
+
+ return true
+end
+
+function _M:encrypt(key, iv, s, no_padding, aead_aad)
+ local _, err = self:init(key, iv, {
+ is_encrypt = true,
+ no_padding = no_padding,
+ })
+ if err then
+ return nil, err
+ end
+ if aead_aad then
+ local _, err = self:update_aead_aad(aead_aad)
+ if err then
+ return nil, err
+ end
+ end
+ return self:final(s)
+end
+
+function _M:decrypt(key, iv, s, no_padding, aead_aad, aead_tag)
+ local _, err = self:init(key, iv, {
+ is_encrypt = false,
+ no_padding = no_padding,
+ })
+ if err then
+ return nil, err
+ end
+ if aead_aad then
+ local _, err = self:update_aead_aad(aead_aad)
+ if err then
+ return nil, err
+ end
+ end
+ if aead_tag then
+ local _, err = self:set_aead_tag(aead_tag)
+ if err then
+ return nil, err
+ end
+ end
+ return self:final(s)
+end
+
+-- https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+function _M:update_aead_aad(aad)
+ if not self.initialized then
+ return nil, "cipher:update_aead_aad: cipher not initalized, call cipher:init first"
+ end
+
+ if C.EVP_CipherUpdate(self.ctx, nil, out_length, aad, #aad) ~= 1 then
+ return false, format_error("cipher:update_aead_aad")
+ end
+ return true
+end
+
+function _M:get_aead_tag(size)
+ if not self.initialized then
+ return nil, "cipher:get_aead_tag: cipher not initalized, call cipher:init first"
+ end
+
+ size = size or self.key_size / 2
+ if size > self.key_size then
+ return nil, string.format("tag size %d is too large", size)
+ end
+ if C.EVP_CIPHER_CTX_ctrl(self.ctx, evp_macro.EVP_CTRL_AEAD_GET_TAG, size, out_buffer) ~= 1 then
+ return nil, format_error("cipher:get_aead_tag")
+ end
+
+ return ffi_str(out_buffer, size)
+end
+
+function _M:set_aead_tag(tag)
+ if not self.initialized then
+ return nil, "cipher:set_aead_tag: cipher not initalized, call cipher:init first"
+ end
+
+ if type(tag) ~= "string" then
+ return false, "cipher:set_aead_tag expect a string at #1"
+ end
+ local tag_void_ptr = ffi_cast(void_ptr, tag)
+ if C.EVP_CIPHER_CTX_ctrl(self.ctx, evp_macro.EVP_CTRL_AEAD_SET_TAG, #tag, tag_void_ptr) ~= 1 then
+ return false, format_error("cipher:set_aead_tag")
+ end
+
+ return true
+end
+
+function _M:update(...)
+ if not self.initialized then
+ return nil, "cipher:update: cipher not initalized, call cipher:init first"
+ end
+
+ local ret = {}
+ for i, s in ipairs({...}) do
+ local inl = #s
+ if inl > 1024 then
+ s = ffi_cast(uchar_ptr, s)
+ for i=0, inl-1, 1024 do
+ local chunk_size = 1024
+ if inl - i < 1024 then
+ chunk_size = inl - i
+ end
+ if C.EVP_CipherUpdate(self.ctx, out_buffer, out_length, s+i, chunk_size) ~= 1 then
+ return nil, format_error("cipher:update")
+ end
+ table.insert(ret, ffi_str(out_buffer, out_length[0]))
+ end
+ else
+ if C.EVP_CipherUpdate(self.ctx, out_buffer, out_length, s, inl) ~= 1 then
+ return nil, format_error("cipher:update")
+ end
+ table.insert(ret, ffi_str(out_buffer, out_length[0]))
+ end
+ end
+ return table.concat(ret, "")
+end
+
+function _M:final(s)
+ local ret, err
+ if s then
+ ret, err = self:update(s)
+ if err then
+ return nil, err
+ end
+ end
+ if C.EVP_CipherFinal_ex(self.ctx, out_buffer, out_length) ~= 1 then
+ return nil, format_error("cipher:final: EVP_CipherFinal_ex")
+ end
+ local final_ret = ffi_str(out_buffer, out_length[0])
+ return ret and (ret .. final_ret) or final_ret
+end
+
+
+function _M:derive(key, salt, count, md, md_properties)
+ if type(key) ~= "string" then
+ return nil, nil, "cipher:derive: expect a string at #1"
+ elseif salt and type(salt) ~= "string" then
+ return nil, nil, "cipher:derive: expect a string at #2"
+ elseif count then
+ count = tonumber(count)
+ if not count then
+ return nil, nil, "cipher:derive: expect a number at #3"
+ end
+ elseif md and type(md) ~= "string" then
+ return nil, nil, "cipher:derive: expect a string or nil at #4"
+ end
+
+ if salt then
+ if #salt > 8 then
+ ngx.log(ngx.WARN, "cipher:derive: salt is too long, truncate salt to 8 bytes")
+ salt = salt:sub(0, 8)
+ elseif #salt < 8 then
+ ngx.log(ngx.WARN, "cipher:derive: salt is too short, padding with zero bytes to length")
+ salt = salt .. string.rep('\000', 8 - #salt)
+ end
+ end
+
+ local mdt
+ if OPENSSL_3X then
+ mdt = C.EVP_MD_fetch(ctx_lib.get_libctx(), md or 'sha1', md_properties)
+ else
+ mdt = C.EVP_get_digestbyname(md or 'sha1')
+ end
+ if mdt == nil then
+ return nil, nil, string.format("cipher:derive: invalid digest type \"%s\"", md)
+ end
+ local cipt = C.EVP_CIPHER_CTX_cipher(self.ctx)
+ local keyb = uchar_array(self.key_size)
+ local ivb = uchar_array(self.iv_size)
+
+ local size = C.EVP_BytesToKey(cipt, mdt, salt,
+ key, #key, count or 1,
+ keyb, ivb)
+ if size == 0 then
+ return nil, nil, format_error("cipher:derive: EVP_BytesToKey")
+ end
+
+ return ffi_str(keyb, size), ffi_str(ivb, self.iv_size)
+end
+
+return _M
diff --git a/server/resty/openssl/ctx.lua b/server/resty/openssl/ctx.lua
new file mode 100644
index 0000000..eaec396
--- /dev/null
+++ b/server/resty/openssl/ctx.lua
@@ -0,0 +1,78 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.ossl_typ"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+ffi.cdef [[
+ OSSL_LIB_CTX *OSSL_LIB_CTX_new(void);
+ int OSSL_LIB_CTX_load_config(OSSL_LIB_CTX *ctx, const char *config_file);
+ void OSSL_LIB_CTX_free(OSSL_LIB_CTX *ctx);
+]]
+
+local ossl_lib_ctx
+
+local function new(request_context_only, conf_file)
+ if not OPENSSL_3X then
+ return false, "ctx is only supported from OpenSSL 3.0"
+ end
+
+ local ctx = C.OSSL_LIB_CTX_new()
+ ffi_gc(ctx, C.OSSL_LIB_CTX_free)
+
+ if conf_file and C.OSSL_LIB_CTX_load_config(ctx, conf_file) ~= 1 then
+ return false, format_error("ctx.new")
+ end
+
+ if request_context_only then
+ ngx.ctx.ossl_lib_ctx = ctx
+ else
+ ossl_lib_ctx = ctx
+ end
+
+ return true
+end
+
+local function free(request_context_only)
+ if not OPENSSL_3X then
+ return false, "ctx is only supported from OpenSSL 3.0"
+ end
+
+ if request_context_only then
+ ngx.ctx.ossl_lib_ctx = nil
+ else
+ ossl_lib_ctx = nil
+ end
+
+ return true
+end
+
+local test_request
+
+do
+
+ local ok, exdata = pcall(require, "thread.exdata")
+ if ok and exdata then
+ test_request = function()
+ local r = exdata()
+ if r ~= nil then
+ return not not r
+ end
+ end
+
+ else
+ local getfenv = getfenv
+
+ function test_request()
+ return not not getfenv(0).__ngx_req
+ end
+ end
+end
+
+return {
+ new = new,
+ free = free,
+ get_libctx = function() return test_request() and ngx.ctx.ossl_lib_ctx or ossl_lib_ctx end,
+} \ No newline at end of file
diff --git a/server/resty/openssl/dh.lua b/server/resty/openssl/dh.lua
new file mode 100644
index 0000000..93e4941
--- /dev/null
+++ b/server/resty/openssl/dh.lua
@@ -0,0 +1,142 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.dh"
+local bn_lib = require "resty.openssl.bn"
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+_M.params = {"public", "private", "p", "q", "g"}
+
+local empty_table = {}
+local bn_ptrptr_ct = ffi.typeof("const BIGNUM *[1]")
+function _M.get_parameters(dh_st)
+ return setmetatable(empty_table, {
+ __index = function(_, k)
+ local ptr, ret
+ if OPENSSL_11_OR_LATER then
+ ptr = bn_ptrptr_ct()
+ end
+
+ if OPENSSL_11_OR_LATER then
+ ptr = bn_ptrptr_ct()
+ end
+
+ if k == 'p' then
+ if OPENSSL_11_OR_LATER then
+ C.DH_get0_pqg(dh_st, ptr, nil, nil)
+ end
+ elseif k == 'q' then
+ if OPENSSL_11_OR_LATER then
+ C.DH_get0_pqg(dh_st, nil, ptr, nil)
+ end
+ elseif k == 'g' then
+ if OPENSSL_11_OR_LATER then
+ C.DH_get0_pqg(dh_st, nil, nil, ptr)
+ end
+ elseif k == 'public' then
+ if OPENSSL_11_OR_LATER then
+ C.DH_get0_key(dh_st, ptr, nil)
+ end
+ k = "pub_key"
+ elseif k == 'private' then
+ if OPENSSL_11_OR_LATER then
+ C.DH_get0_key(dh_st, nil, ptr)
+ end
+ k = "priv_key"
+ else
+ return nil, "rsa.get_parameters: unknown parameter \"" .. k .. "\" for RSA key"
+ end
+
+ if OPENSSL_11_OR_LATER then
+ ret = ptr[0]
+ elseif OPENSSL_10 then
+ ret = dh_st[k]
+ end
+
+ if ret == nil then
+ return nil
+ end
+ return bn_lib.dup(ret)
+ end
+ }), nil
+end
+
+local function dup_bn_value(v)
+ if not bn_lib.istype(v) then
+ return nil, "expect value to be a bn instance"
+ end
+ local bn = C.BN_dup(v.ctx)
+ if bn == nil then
+ return nil, "BN_dup() failed"
+ end
+ return bn
+end
+
+function _M.set_parameters(dh_st, opts)
+ local err
+ local opts_bn = {}
+ -- remember which parts of BNs has been added to dh_st, they should be freed
+ -- by DH_free and we don't cleanup them on failure
+ local cleanup_from_idx = 1
+ -- dup input
+ local do_set_key, do_set_pqg
+ for k, v in pairs(opts) do
+ opts_bn[k], err = dup_bn_value(v)
+ if err then
+ err = "dh.set_parameters: cannot process parameter \"" .. k .. "\":" .. err
+ goto cleanup_with_error
+ end
+ if k == "private" or k == "public" then
+ do_set_key = true
+ elseif k == "p" or k == "q" or k == "g" then
+ do_set_pqg = true
+ end
+ end
+ if OPENSSL_11_OR_LATER then
+ local code
+ if do_set_key then
+ code = C.DH_set0_key(dh_st, opts_bn["public"], opts_bn["private"])
+ if code == 0 then
+ err = format_error("dh.set_parameters: DH_set0_key")
+ goto cleanup_with_error
+ end
+ end
+ cleanup_from_idx = cleanup_from_idx + 2
+ if do_set_pqg then
+ code = C.DH_set0_pqg(dh_st, opts_bn["p"], opts_bn["q"], opts_bn["g"])
+ if code == 0 then
+ err = format_error("dh.set_parameters: DH_set0_pqg")
+ goto cleanup_with_error
+ end
+ end
+ return true
+ elseif OPENSSL_10 then
+ for k, v in pairs(opts_bn) do
+ if k == "public" then
+ k = "pub_key"
+ elseif k == "private" then
+ k = "priv_key"
+ end
+ if dh_st[k] ~= nil then
+ C.BN_free(dh_st[k])
+ end
+ dh_st[k]= v
+ end
+ return true
+ end
+
+::cleanup_with_error::
+ for i, k in pairs(_M.params) do
+ if i >= cleanup_from_idx then
+ C.BN_free(opts_bn[k])
+ end
+ end
+ return false, err
+end
+
+return _M
diff --git a/server/resty/openssl/digest.lua b/server/resty/openssl/digest.lua
new file mode 100644
index 0000000..cfef9ae
--- /dev/null
+++ b/server/resty/openssl/digest.lua
@@ -0,0 +1,116 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.evp.md"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = {__index = _M}
+
+local md_ctx_ptr_ct = ffi.typeof('EVP_MD_CTX*')
+
+function _M.new(typ, properties)
+ local ctx
+ if OPENSSL_11_OR_LATER then
+ ctx = C.EVP_MD_CTX_new()
+ ffi_gc(ctx, C.EVP_MD_CTX_free)
+ elseif OPENSSL_10 then
+ ctx = C.EVP_MD_CTX_create()
+ ffi_gc(ctx, C.EVP_MD_CTX_destroy)
+ end
+ if ctx == nil then
+ return nil, "digest.new: failed to create EVP_MD_CTX"
+ end
+
+ local err_new = string.format("digest.new: invalid digest type \"%s\"", typ)
+
+ local algo
+ if typ == "null" then
+ algo = C.EVP_md_null()
+ else
+ if OPENSSL_3X then
+ algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), typ or 'sha1', properties)
+ else
+ algo = C.EVP_get_digestbyname(typ or 'sha1')
+ end
+ if algo == nil then
+ return nil, format_error(err_new)
+ end
+ end
+
+ local code = C.EVP_DigestInit_ex(ctx, algo, nil)
+ if code ~= 1 then
+ return nil, format_error(err_new)
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ algo = algo,
+ buf = ctypes.uchar_array(OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)),
+ }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(md_ctx_ptr_ct, l.ctx)
+end
+
+function _M:get_provider_name()
+ if not OPENSSL_3X then
+ return false, "digest:get_provider_name is not supported"
+ end
+ local p = C.EVP_MD_get0_provider(self.algo)
+ if p == nil then
+ return nil
+ end
+ return ffi_str(C.OSSL_PROVIDER_get0_name(p))
+end
+
+if OPENSSL_3X then
+ local param_lib = require "resty.openssl.param"
+ _M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_MD_CTX")
+end
+
+function _M:update(...)
+ for _, s in ipairs({...}) do
+ if C.EVP_DigestUpdate(self.ctx, s, #s) ~= 1 then
+ return false, format_error("digest:update")
+ end
+ end
+ return true, nil
+end
+
+local result_length = ctypes.ptr_of_uint()
+
+function _M:final(s)
+ if s then
+ if C.EVP_DigestUpdate(self.ctx, s, #s) ~= 1 then
+ return false, format_error("digest:final")
+ end
+ end
+
+ -- no return value of EVP_DigestFinal_ex
+ C.EVP_DigestFinal_ex(self.ctx, self.buf, result_length)
+ if result_length[0] == nil or result_length[0] <= 0 then
+ return nil, format_error("digest:final: EVP_DigestFinal_ex")
+ end
+ return ffi_str(self.buf, result_length[0])
+end
+
+
+function _M:reset()
+ local code = C.EVP_DigestInit_ex(self.ctx, self.algo, nil)
+ if code ~= 1 then
+ return false, format_error("digest:reset")
+ end
+
+ return true
+end
+
+return _M
diff --git a/server/resty/openssl/ec.lua b/server/resty/openssl/ec.lua
new file mode 100644
index 0000000..2d0dd02
--- /dev/null
+++ b/server/resty/openssl/ec.lua
@@ -0,0 +1,186 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.ec"
+local bn_lib = require "resty.openssl.bn"
+local objects_lib = require "resty.openssl.objects"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+
+local version_num = require("resty.openssl.version").version_num
+local format_error = require("resty.openssl.err").format_error
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local _M = {}
+
+_M.params = {"group", "public", "private", "x", "y"}
+
+local empty_table = {}
+
+function _M.get_parameters(ec_key_st)
+ return setmetatable(empty_table, {
+ __index = function(_, k)
+ local group = C.EC_KEY_get0_group(ec_key_st)
+ local bn
+
+ if k == 'group' then
+ local nid = C.EC_GROUP_get_curve_name(group)
+ if nid == 0 then
+ return nil, "ec.get_parameters: EC_GROUP_get_curve_name() failed"
+ end
+ return nid
+ elseif k == 'public' or k == "pub_key" then
+ local pub_point = C.EC_KEY_get0_public_key(ec_key_st)
+ if pub_point == nil then
+ return nil, format_error("ec.get_parameters: EC_KEY_get0_public_key")
+ end
+ local point_form = C.EC_KEY_get_conv_form(ec_key_st)
+ if point_form == nil then
+ return nil, format_error("ec.get_parameters: EC_KEY_get_conv_form")
+ end
+ if BORINGSSL then
+ local sz = tonumber(C.EC_POINT_point2oct(group, pub_point, point_form, nil, 0, bn_lib.bn_ctx_tmp))
+ if sz <= 0 then
+ return nil, format_error("ec.get_parameters: EC_POINT_point2oct")
+ end
+ local buf = ctypes.uchar_array(sz)
+ C.EC_POINT_point2oct(group, pub_point, point_form, buf, sz, bn_lib.bn_ctx_tmp)
+ buf = ffi.string(buf, sz)
+ local err
+ bn, err = bn_lib.from_binary(buf)
+ if bn == nil then
+ return nil, "ec.get_parameters: bn_lib.from_binary: " .. err
+ end
+ return bn
+ else
+ bn = C.EC_POINT_point2bn(group, pub_point, point_form, nil, bn_lib.bn_ctx_tmp)
+ if bn == nil then
+ return nil, format_error("ec.get_parameters: EC_POINT_point2bn")
+ end
+ ffi_gc(bn, C.BN_free)
+ end
+ elseif k == 'private' or k == "priv_key" then
+ -- get0, don't GC
+ bn = C.EC_KEY_get0_private_key(ec_key_st)
+ elseif k == 'x' or k == 'y' then
+ local pub_point = C.EC_KEY_get0_public_key(ec_key_st)
+ if pub_point == nil then
+ return nil, format_error("ec.get_parameters: EC_KEY_get0_public_key")
+ end
+ bn = C.BN_new()
+ if bn == nil then
+ return nil, "ec.get_parameters: BN_new() failed"
+ end
+ ffi_gc(bn, C.BN_free)
+ local f
+ if version_num >= 0x10101000 then
+ f = C.EC_POINT_get_affine_coordinates
+ else
+ f = C.EC_POINT_get_affine_coordinates_GFp
+ end
+ local code
+ if k == 'x' then
+ code = f(group, pub_point, bn, nil, bn_lib.bn_ctx_tmp)
+ else
+ code = f(group, pub_point, nil, bn, bn_lib.bn_ctx_tmp)
+ end
+ if code ~= 1 then
+ return nil, format_error("ec.get_parameters: EC_POINT_get_affine_coordinates")
+ end
+ else
+ return nil, "ec.get_parameters: unknown parameter \"" .. k .. "\" for EC key"
+ end
+
+ if bn == nil then
+ return nil
+ end
+ return bn_lib.dup(bn)
+ end
+ }), nil
+end
+
+function _M.set_parameters(ec_key_st, opts)
+ for _, k in ipairs(_M.params) do
+ if k ~= "group" then
+ if opts[k] and not bn_lib.istype(opts[k]) then
+ return nil, "expect parameter \"" .. k .. "\" to be a bn instance"
+ end
+ end
+ end
+
+ local group_nid = opts["group"]
+ local group
+ if group_nid then
+ local nid, err = objects_lib.txtnid2nid(group_nid)
+ if err then
+ return nil, "ec.set_parameters: cannot use parameter \"group\":" .. err
+ end
+
+ group = C.EC_GROUP_new_by_curve_name(nid)
+ if group == nil then
+ return nil, "ec.set_parameters: EC_GROUP_new_by_curve_name() failed"
+ end
+ ffi_gc(group, C.EC_GROUP_free)
+ -- # define OPENSSL_EC_NAMED_CURVE 0x001
+ C.EC_GROUP_set_asn1_flag(group, 1)
+ C.EC_GROUP_set_point_conversion_form(group, C.POINT_CONVERSION_UNCOMPRESSED)
+
+ if C.EC_KEY_set_group(ec_key_st, group) ~= 1 then
+ return nil, format_error("ec.set_parameters: EC_KEY_set_group")
+ end
+ end
+
+ local x = opts["x"]
+ local y = opts["y"]
+ local pub = opts["public"]
+ if (x and not y) or (y and not x) then
+ return nil, "ec.set_parameters: \"x\" and \"y\" parameter must be defined at same time or both undefined"
+ end
+
+ if x and y then
+ if pub then
+ return nil, "ec.set_parameters: cannot set \"x\" and \"y\" with \"public\" at same time to set public key"
+ end
+ -- double check if we have set group already
+ if group == nil then
+ group = C.EC_KEY_get0_group(ec_key_st)
+ if group == nil then
+ return nil, "ec.set_parameters: cannot set public key without setting \"group\""
+ end
+ end
+
+ if C.EC_KEY_set_public_key_affine_coordinates(ec_key_st, x.ctx, y.ctx) ~= 1 then
+ return nil, format_error("ec.set_parameters: EC_KEY_set_public_key_affine_coordinates")
+ end
+ end
+
+ if pub then
+ if group == nil then
+ group = C.EC_KEY_get0_group(ec_key_st)
+ if group == nil then
+ return nil, "ec.set_parameters: cannot set public key without setting \"group\""
+ end
+ end
+
+ local point = C.EC_POINT_bn2point(group, pub.ctx, nil, bn_lib.bn_ctx_tmp)
+ if point == nil then
+ return nil, format_error("ec.set_parameters: EC_POINT_bn2point")
+ end
+ ffi_gc(point, C.EC_POINT_free)
+
+ if C.EC_KEY_set_public_key(ec_key_st, point) ~= 1 then
+ return nil, format_error("ec.set_parameters: EC_KEY_set_public_key")
+ end
+ end
+
+ local priv = opts["private"]
+ if priv then
+ -- openssl duplicates it inside
+ if C.EC_KEY_set_private_key(ec_key_st, priv.ctx) ~= 1 then
+ return nil, format_error("ec.set_parameters: EC_KEY_set_private_key")
+ end
+ end
+
+end
+
+return _M
diff --git a/server/resty/openssl/ecx.lua b/server/resty/openssl/ecx.lua
new file mode 100644
index 0000000..5ec7162
--- /dev/null
+++ b/server/resty/openssl/ecx.lua
@@ -0,0 +1,67 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+
+require "resty.openssl.include.ec"
+require "resty.openssl.include.evp"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+_M.params = {"public", "private"}
+
+local empty_table = {}
+
+local MAX_ECX_KEY_SIZE = 114 -- ed448 uses 114 bytes
+
+function _M.get_parameters(evp_pkey_st)
+ return setmetatable(empty_table, {
+ __index = function(_, k)
+ local buf = ctypes.uchar_array(MAX_ECX_KEY_SIZE)
+ local length = ctypes.ptr_of_size_t(MAX_ECX_KEY_SIZE)
+
+ if k == 'public' or k == "pub_key" then
+ if C.EVP_PKEY_get_raw_public_key(evp_pkey_st, buf, length) ~= 1 then
+ error(format_error("ecx.get_parameters: EVP_PKEY_get_raw_private_key"))
+ end
+ elseif k == 'private' or k == "priv ~=_key" then
+ if C.EVP_PKEY_get_raw_private_key(evp_pkey_st, buf, length) ~= 1 then
+ return nil, format_error("ecx.get_parameters: EVP_PKEY_get_raw_private_key")
+ end
+ else
+ return nil, "ecx.get_parameters: unknown parameter \"" .. k .. "\" for EC key"
+ end
+ return ffi_str(buf, length[0])
+ end
+ }), nil
+end
+
+function _M.set_parameters(key_type, evp_pkey_st, opts)
+ -- for ecx keys we always create a new EVP_PKEY and release the old one
+ -- Note: we allow to pass a nil as evp_pkey_st to create a new EVP_PKEY
+ local key
+ if opts.private then
+ local priv = opts.private
+ key = C.EVP_PKEY_new_raw_private_key(key_type, nil, priv, #priv)
+ if key == nil then
+ return nil, format_error("ecx.set_parameters: EVP_PKEY_new_raw_private_key")
+ end
+ elseif opts.public then
+ local pub = opts.public
+ key = C.EVP_PKEY_new_raw_public_key(key_type, nil, pub, #pub)
+ if key == nil then
+ return nil, format_error("ecx.set_parameters: EVP_PKEY_new_raw_public_key")
+ end
+ else
+ return nil, "no parameter is specified"
+ end
+
+ if evp_pkey_st ~= nil then
+ C.EVP_PKEY_free(evp_pkey_st)
+ end
+ return key
+
+end
+
+return _M
diff --git a/server/resty/openssl/err.lua b/server/resty/openssl/err.lua
new file mode 100644
index 0000000..a047a7c
--- /dev/null
+++ b/server/resty/openssl/err.lua
@@ -0,0 +1,62 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+local ffi_sizeof = ffi.sizeof
+
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+require "resty.openssl.include.err"
+
+local constchar_ptrptr = ffi.typeof("const char*[1]")
+
+local buf = ffi.new('char[256]')
+
+local function format_error(ctx, code, all_errors)
+ local errors = {}
+ if code then
+ table.insert(errors, string.format("code: %d", code or 0))
+ end
+ -- get the OpenSSL errors
+ while C.ERR_peek_error() ~= 0 do
+ local line = ctypes.ptr_of_int()
+ local path = constchar_ptrptr()
+ local code
+ if all_errors then
+ code = C.ERR_get_error_line(path, line)
+ else
+ code = C.ERR_peek_last_error_line(path, line)
+ end
+
+ local abs_path = ffi_str(path[0])
+ -- ../crypto/asn1/a_d2i_fp.c => crypto/asn1/a_d2i_fp.c
+ local start = abs_path:find("/")
+ if start then
+ abs_path = abs_path:sub(start+1)
+ end
+
+ C.ERR_error_string_n(code, buf, ffi_sizeof(buf))
+ table.insert(errors, string.format("%s:%d:%s",
+ abs_path, line[0], ffi_str(buf))
+ )
+
+ if not all_errors then
+ break
+ end
+ end
+
+ C.ERR_clear_error()
+
+ if #errors > 0 then
+ return string.format("%s%s%s", (ctx or ""), (ctx and ": " or ""), table.concat(errors, " "))
+ else
+ return string.format("%s failed", ctx)
+ end
+end
+
+local function format_all_error(ctx, code)
+ return format_error(ctx, code, true)
+end
+
+return {
+ format_error = format_error,
+ format_all_error = format_all_error,
+} \ No newline at end of file
diff --git a/server/resty/openssl/hmac.lua b/server/resty/openssl/hmac.lua
new file mode 100644
index 0000000..fe18d2f
--- /dev/null
+++ b/server/resty/openssl/hmac.lua
@@ -0,0 +1,90 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.hmac"
+require "resty.openssl.include.evp.md"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = {__index = _M}
+
+local hmac_ctx_ptr_ct = ffi.typeof('HMAC_CTX*')
+
+-- Note: https://www.openssl.org/docs/manmaster/man3/HMAC_Init.html
+-- Replace with EVP_MAC_* functions for OpenSSL 3.0
+
+function _M.new(key, typ)
+ local ctx
+ if OPENSSL_11_OR_LATER then
+ ctx = C.HMAC_CTX_new()
+ ffi_gc(ctx, C.HMAC_CTX_free)
+ elseif OPENSSL_10 then
+ ctx = ffi.new('HMAC_CTX')
+ C.HMAC_CTX_init(ctx)
+ ffi_gc(ctx, C.HMAC_CTX_cleanup)
+ end
+ if ctx == nil then
+ return nil, "hmac.new: failed to create HMAC_CTX"
+ end
+
+ local algo = C.EVP_get_digestbyname(typ or 'sha1')
+ if algo == nil then
+ return nil, string.format("hmac.new: invalid digest type \"%s\"", typ)
+ end
+
+ local code = C.HMAC_Init_ex(ctx, key, #key, algo, nil)
+ if code ~= 1 then
+ return nil, format_error("hmac.new")
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ algo = algo,
+ buf = ctypes.uchar_array(OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)),
+ }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(hmac_ctx_ptr_ct, l.ctx)
+end
+
+function _M:update(...)
+ for _, s in ipairs({...}) do
+ if C.HMAC_Update(self.ctx, s, #s) ~= 1 then
+ return false, format_error("hmac:update")
+ end
+ end
+ return true, nil
+end
+
+local result_length = ctypes.ptr_of_uint()
+
+function _M:final(s)
+ if s then
+ if C.HMAC_Update(self.ctx, s, #s) ~= 1 then
+ return false, format_error("hmac:final")
+ end
+ end
+
+ if C.HMAC_Final(self.ctx, self.buf, result_length) ~= 1 then
+ return nil, format_error("hmac:final: HMAC_Final")
+ end
+ return ffi_str(self.buf, result_length[0])
+end
+
+function _M:reset()
+ local code = C.HMAC_Init_ex(self.ctx, nil, 0, nil, nil)
+ if code ~= 1 then
+ return false, format_error("hmac:reset")
+ end
+
+ return true
+end
+
+return _M
diff --git a/server/resty/openssl/include/asn1.lua b/server/resty/openssl/include/asn1.lua
new file mode 100644
index 0000000..ba59ebc
--- /dev/null
+++ b/server/resty/openssl/include/asn1.lua
@@ -0,0 +1,94 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+ffi.cdef [[
+ typedef struct ASN1_VALUE_st ASN1_VALUE;
+
+ typedef struct asn1_type_st ASN1_TYPE;
+
+ ASN1_IA5STRING *ASN1_IA5STRING_new();
+
+ int ASN1_STRING_type(const ASN1_STRING *x);
+ ASN1_STRING *ASN1_STRING_type_new(int type);
+ int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
+
+ ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
+ BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn);
+
+ typedef int time_t;
+ ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
+
+ int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+ long ASN1_INTEGER_get(const ASN1_INTEGER *a);
+ int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v);
+
+ int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
+
+ int ASN1_STRING_length(const ASN1_STRING *x);
+]]
+
+local function declare_asn1_functions(typ, has_ex)
+ local t = {}
+ for i=1, 7 do
+ t[i] = typ
+ end
+
+ ffi.cdef(string.format([[
+ %s *%s_new(void);
+ void %s_free(%s *a);
+ %s *%s_dup(%s *a);
+ ]], unpack(t)))
+
+ if OPENSSL_3X and has_ex then
+ ffi.cdef(string.format([[
+ %s *%s_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
+ ]], typ, typ))
+ end
+end
+
+declare_asn1_functions("ASN1_INTEGER")
+declare_asn1_functions("ASN1_OBJECT")
+declare_asn1_functions("ASN1_STRING")
+declare_asn1_functions("ASN1_ENUMERATED")
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
+
+local ASN1_STRING_get0_data
+if OPENSSL_11_OR_LATER then
+ ffi.cdef[[
+ const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x);
+ ]]
+ ASN1_STRING_get0_data = C.ASN1_STRING_get0_data
+elseif OPENSSL_10 then
+ ffi.cdef[[
+ unsigned char *ASN1_STRING_data(ASN1_STRING *x);
+ typedef struct ASN1_ENCODING_st {
+ unsigned char *enc; /* DER encoding */
+ long len; /* Length of encoding */
+ int modified; /* set to 1 if 'enc' is invalid */
+ } ASN1_ENCODING;
+ ]]
+ ASN1_STRING_get0_data = C.ASN1_STRING_data
+end
+
+if BORINGSSL_110 then
+ ffi.cdef [[
+ // required by resty/openssl/include/x509/crl.lua
+ typedef struct ASN1_ENCODING_st {
+ unsigned char *enc; /* DER encoding */
+ long len; /* Length of encoding */
+ int modified; /* set to 1 if 'enc' is invalid */
+ } ASN1_ENCODING;
+ ]]
+end
+
+return {
+ ASN1_STRING_get0_data = ASN1_STRING_get0_data,
+ declare_asn1_functions = declare_asn1_functions,
+ has_new_ex = true,
+}
diff --git a/server/resty/openssl/include/bio.lua b/server/resty/openssl/include/bio.lua
new file mode 100644
index 0000000..45297fc
--- /dev/null
+++ b/server/resty/openssl/include/bio.lua
@@ -0,0 +1,13 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ typedef struct bio_method_st BIO_METHOD;
+ long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg);
+ BIO *BIO_new_mem_buf(const void *buf, int len);
+ BIO *BIO_new(const BIO_METHOD *type);
+ int BIO_free(BIO *a);
+ const BIO_METHOD *BIO_s_mem(void);
+ int BIO_read(BIO *b, void *data, int dlen);
+]]
diff --git a/server/resty/openssl/include/bn.lua b/server/resty/openssl/include/bn.lua
new file mode 100644
index 0000000..93d2dda
--- /dev/null
+++ b/server/resty/openssl/include/bn.lua
@@ -0,0 +1,77 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local BN_ULONG
+if ffi.abi('64bit') then
+ BN_ULONG = 'unsigned long long'
+else -- 32bit
+ BN_ULONG = 'unsigned int'
+end
+
+ffi.cdef(
+[[
+ BIGNUM *BN_new(void);
+ void BN_free(BIGNUM *a);
+
+ BN_CTX *BN_CTX_new(void);
+ void BN_CTX_init(BN_CTX *c);
+ void BN_CTX_free(BN_CTX *c);
+
+ BIGNUM *BN_dup(const BIGNUM *a);
+ int BN_add_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
+ int BN_set_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
+ ]] .. BN_ULONG ..[[ BN_get_word(BIGNUM *a);
+ int BN_num_bits(const BIGNUM *a);
+ BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
+ int BN_hex2bn(BIGNUM **a, const char *str);
+ int BN_dec2bn(BIGNUM **a, const char *str);
+ int BN_bn2bin(const BIGNUM *a, unsigned char *to);
+ char *BN_bn2hex(const BIGNUM *a);
+ char *BN_bn2dec(const BIGNUM *a);
+
+ void BN_set_negative(BIGNUM *a, int n);
+ int BN_is_negative(const BIGNUM *a);
+
+ int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+ int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
+ int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx);
+ int BN_sqr(BIGNUM *r, BIGNUM *a, BN_CTX *ctx);
+ int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *a, const BIGNUM *d,
+ BN_CTX *ctx);
+ int BN_mod_add(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
+ BN_CTX *ctx);
+ int BN_mod_sub(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
+ BN_CTX *ctx);
+ int BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
+ BN_CTX *ctx);
+ int BN_mod_sqr(BIGNUM *ret, BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
+ int BN_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p, BN_CTX *ctx);
+ int BN_mod_exp(BIGNUM *r, BIGNUM *a, const BIGNUM *p,
+ const BIGNUM *m, BN_CTX *ctx);
+ int BN_gcd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx);
+
+ int BN_lshift(BIGNUM *r, const BIGNUM *a, int n);
+ int BN_rshift(BIGNUM *r, BIGNUM *a, int n);
+
+ int BN_cmp(BIGNUM *a, BIGNUM *b);
+ int BN_ucmp(BIGNUM *a, BIGNUM *b);
+
+ // openssl >= 1.1 only
+ int BN_is_zero(BIGNUM *a);
+ int BN_is_one(BIGNUM *a);
+ int BN_is_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
+ int BN_is_odd(BIGNUM *a);
+
+ int BN_is_prime_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, BN_GENCB *cb);
+ int BN_generate_prime_ex(BIGNUM *ret,int bits,int safe, const BIGNUM *add,
+ const BIGNUM *rem, BN_GENCB *cb);
+]]
+)
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ int BN_check_prime(const BIGNUM *p, BN_CTX *ctx, BN_GENCB *cb);
+ ]]
+end \ No newline at end of file
diff --git a/server/resty/openssl/include/conf.lua b/server/resty/openssl/include/conf.lua
new file mode 100644
index 0000000..d655993
--- /dev/null
+++ b/server/resty/openssl/include/conf.lua
@@ -0,0 +1,9 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ CONF *NCONF_new(CONF_METHOD *meth);
+ void NCONF_free(CONF *conf);
+ int NCONF_load_bio(CONF *conf, BIO *bp, long *eline);
+]] \ No newline at end of file
diff --git a/server/resty/openssl/include/crypto.lua b/server/resty/openssl/include/crypto.lua
new file mode 100644
index 0000000..6ca1f08
--- /dev/null
+++ b/server/resty/openssl/include/crypto.lua
@@ -0,0 +1,31 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+
+local OPENSSL_free
+if OPENSSL_10 then
+ ffi.cdef [[
+ void CRYPTO_free(void *ptr);
+ ]]
+ OPENSSL_free = C.CRYPTO_free
+elseif OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ void CRYPTO_free(void *ptr, const char *file, int line);
+ ]]
+ OPENSSL_free = function(ptr)
+ -- file and line is for debuggin only, since we can't know the c file info
+ -- the macro is expanded, just ignore this
+ C.CRYPTO_free(ptr, "", 0)
+ end
+end
+
+ffi.cdef [[
+ int FIPS_mode(void);
+ int FIPS_mode_set(int ONOFF);
+]]
+
+return {
+ OPENSSL_free = OPENSSL_free,
+}
diff --git a/server/resty/openssl/include/dh.lua b/server/resty/openssl/include/dh.lua
new file mode 100644
index 0000000..504879d
--- /dev/null
+++ b/server/resty/openssl/include/dh.lua
@@ -0,0 +1,80 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.objects"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ void DH_get0_pqg(const DH *dh,
+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
+ int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
+ void DH_get0_key(const DH *dh,
+ const BIGNUM **pub_key, const BIGNUM **priv_key);
+ int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ struct dh_st {
+ /*
+ * This first argument is used to pick up errors when a DH is passed
+ * instead of a EVP_PKEY
+ */
+ int pad;
+ int version;
+ BIGNUM *p;
+ BIGNUM *g;
+ long length; /* optional */
+ BIGNUM *pub_key; /* g^x */
+ BIGNUM *priv_key; /* x */
+ int flags;
+ /*BN_MONT_CTX*/ void *method_mont_p;
+ /* Place holders if we want to do X9.42 DH */
+ BIGNUM *q;
+ BIGNUM *j;
+ unsigned char *seed;
+ int seedlen;
+ BIGNUM *counter;
+ int references;
+ /* trimmer */
+ // CRYPTO_EX_DATA ex_data;
+ // const DH_METHOD *meth;
+ // ENGINE *engine;
+ };
+ ]]
+end
+
+ffi.cdef [[
+ DH *DH_get_1024_160(void);
+ DH *DH_get_2048_224(void);
+ DH *DH_get_2048_256(void);
+ DH *DH_new_by_nid(int nid);
+]];
+
+
+local dh_groups = {
+ -- per https://tools.ietf.org/html/rfc5114
+ dh_1024_160 = function() return C.DH_get_1024_160() end,
+ dh_2048_224 = function() return C.DH_get_2048_224() end,
+ dh_2048_256 = function() return C.DH_get_2048_256() end,
+}
+
+local groups = {
+ "ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144", "ffdhe8192",
+ "modp_2048", "modp_3072", "modp_4096", "modp_6144", "modp_8192",
+ -- following cannot be used with FIPS provider
+ "modp_1536", -- and the RFC5114 ones
+}
+
+for _, group in ipairs(groups) do
+ local nid = C.OBJ_sn2nid(group)
+ if nid ~= 0 then
+ dh_groups[group] = function() return C.DH_new_by_nid(nid) end
+ end
+end
+
+return {
+ dh_groups = dh_groups,
+}
diff --git a/server/resty/openssl/include/ec.lua b/server/resty/openssl/include/ec.lua
new file mode 100644
index 0000000..674ef42
--- /dev/null
+++ b/server/resty/openssl/include/ec.lua
@@ -0,0 +1,59 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ /** Enum for the point conversion form as defined in X9.62 (ECDSA)
+ * for the encoding of a elliptic curve point (x,y) */
+ typedef enum {
+ /** the point is encoded as z||x, where the octet z specifies
+ * which solution of the quadratic equation y is */
+ POINT_CONVERSION_COMPRESSED = 2,
+ /** the point is encoded as z||x||y, where z is the octet 0x04 */
+ POINT_CONVERSION_UNCOMPRESSED = 4,
+ /** the point is encoded as z||x||y, where the octet z specifies
+ * which solution of the quadratic equation y is */
+ POINT_CONVERSION_HYBRID = 6
+ } point_conversion_form_t;
+
+ EC_KEY *EC_KEY_new(void);
+ void EC_KEY_free(EC_KEY *key);
+
+ EC_GROUP *EC_GROUP_new_by_curve_name(int nid);
+ void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag);
+ void EC_GROUP_set_point_conversion_form(EC_GROUP *group,
+ point_conversion_form_t form);
+ void EC_GROUP_set_curve_name(EC_GROUP *group, int nid);
+ int EC_GROUP_get_curve_name(const EC_GROUP *group);
+
+
+ void EC_GROUP_free(EC_GROUP *group);
+
+ BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *,
+ point_conversion_form_t form, BIGNUM *, BN_CTX *);
+ // for BoringSSL
+ size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *p,
+ point_conversion_form_t form,
+ unsigned char *buf, size_t len, BN_CTX *ctx);
+ // OpenSSL < 1.1.1
+ int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group,
+ const EC_POINT *p,
+ BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
+ // OpenSSL >= 1.1.1
+ int EC_POINT_get_affine_coordinates(const EC_GROUP *group, const EC_POINT *p,
+ BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
+ EC_POINT *EC_POINT_bn2point(const EC_GROUP *group, const BIGNUM *bn,
+ EC_POINT *p, BN_CTX *ctx);
+
+ point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key);
+
+ const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key);
+ int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv);
+
+ const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
+ int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
+ int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x, BIGNUM *y);
+
+ const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
+ int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
+]]
diff --git a/server/resty/openssl/include/err.lua b/server/resty/openssl/include/err.lua
new file mode 100644
index 0000000..142098c
--- /dev/null
+++ b/server/resty/openssl/include/err.lua
@@ -0,0 +1,9 @@
+local ffi = require "ffi"
+
+ffi.cdef [[
+ unsigned long ERR_peek_error(void);
+ unsigned long ERR_peek_last_error_line(const char **file, int *line);
+ unsigned long ERR_get_error_line(const char **file, int *line);
+ void ERR_clear_error(void);
+ void ERR_error_string_n(unsigned long e, char *buf, size_t len);
+]]
diff --git a/server/resty/openssl/include/evp.lua b/server/resty/openssl/include/evp.lua
new file mode 100644
index 0000000..beeaf91
--- /dev/null
+++ b/server/resty/openssl/include/evp.lua
@@ -0,0 +1,109 @@
+local ffi = require "ffi"
+local C = ffi.C
+local bit = require("bit")
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.err"
+require "resty.openssl.include.objects"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+if BORINGSSL then
+ ffi.cdef [[
+ int PKCS5_PBKDF2_HMAC(const char *password, size_t password_len,
+ const uint8_t *salt, size_t salt_len,
+ unsigned iterations, const EVP_MD *digest,
+ size_t key_len, uint8_t *out_key);
+ int EVP_PBE_scrypt(const char *password, size_t password_len,
+ const uint8_t *salt, size_t salt_len,
+ uint64_t N, uint64_t r, uint64_t p,
+ size_t max_mem, uint8_t *out_key,
+ size_t key_len);
+ ]]
+else
+ ffi.cdef [[
+ /* KDF */
+ int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
+ const unsigned char *salt, int saltlen, int iter,
+ const EVP_MD *digest, int keylen, unsigned char *out);
+
+ int EVP_PBE_scrypt(const char *pass, size_t passlen,
+ const unsigned char *salt, size_t saltlen,
+ uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem,
+ unsigned char *key, size_t keylen);
+ ]]
+end
+
+if OPENSSL_3X then
+ require "resty.openssl.include.provider"
+
+ ffi.cdef [[
+ int EVP_set_default_properties(OSSL_LIB_CTX *libctx, const char *propq);
+ int EVP_default_properties_enable_fips(OSSL_LIB_CTX *libctx, int enable);
+ int EVP_default_properties_is_fips_enabled(OSSL_LIB_CTX *libctx);
+
+ // const OSSL_PROVIDER *EVP_RAND_get0_provider(const EVP_RAND *rand);
+ // EVP_RAND *EVP_RAND_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
+ // const char *properties);
+ ]]
+end
+
+local EVP_PKEY_ALG_CTRL = 0x1000
+
+local _M = {
+ EVP_PKEY_RSA = C.OBJ_txt2nid("rsaEncryption"),
+ EVP_PKEY_DH = C.OBJ_txt2nid("dhKeyAgreement"),
+ EVP_PKEY_EC = C.OBJ_txt2nid("id-ecPublicKey"),
+ EVP_PKEY_X25519 = C.OBJ_txt2nid("X25519"),
+ EVP_PKEY_ED25519 = C.OBJ_txt2nid("ED25519"),
+ EVP_PKEY_X448 = C.OBJ_txt2nid("X448"),
+ EVP_PKEY_ED448 = C.OBJ_txt2nid("ED448"),
+
+ EVP_PKEY_OP_PARAMGEN = bit.lshift(1, 1),
+ EVP_PKEY_OP_KEYGEN = bit.lshift(1, 2),
+ EVP_PKEY_OP_SIGN = bit.lshift(1, 3),
+ EVP_PKEY_OP_VERIFY = bit.lshift(1, 4),
+ EVP_PKEY_OP_DERIVE = OPENSSL_3X and bit.lshift(1, 12) or bit.lshift(1, 10),
+
+ EVP_PKEY_ALG_CTRL = EVP_PKEY_ALG_CTRL,
+
+
+ EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN = EVP_PKEY_ALG_CTRL + 1,
+ EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID = EVP_PKEY_ALG_CTRL + 1,
+ EVP_PKEY_CTRL_EC_PARAM_ENC = EVP_PKEY_ALG_CTRL + 2,
+ EVP_PKEY_CTRL_RSA_KEYGEN_BITS = EVP_PKEY_ALG_CTRL + 3,
+ EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP = EVP_PKEY_ALG_CTRL + 4,
+ EVP_PKEY_CTRL_RSA_PADDING = EVP_PKEY_ALG_CTRL + 1,
+ EVP_PKEY_CTRL_RSA_PSS_SALTLEN = EVP_PKEY_ALG_CTRL + 2,
+
+ EVP_CTRL_AEAD_SET_IVLEN = 0x9,
+ EVP_CTRL_AEAD_GET_TAG = 0x10,
+ EVP_CTRL_AEAD_SET_TAG = 0x11,
+
+ EVP_PKEY_CTRL_TLS_MD = EVP_PKEY_ALG_CTRL,
+ EVP_PKEY_CTRL_TLS_SECRET = EVP_PKEY_ALG_CTRL + 1,
+ EVP_PKEY_CTRL_TLS_SEED = EVP_PKEY_ALG_CTRL + 2,
+ EVP_PKEY_CTRL_HKDF_MD = EVP_PKEY_ALG_CTRL + 3,
+ EVP_PKEY_CTRL_HKDF_SALT = EVP_PKEY_ALG_CTRL + 4,
+ EVP_PKEY_CTRL_HKDF_KEY = EVP_PKEY_ALG_CTRL + 5,
+ EVP_PKEY_CTRL_HKDF_INFO = EVP_PKEY_ALG_CTRL + 6,
+ EVP_PKEY_CTRL_HKDF_MODE = EVP_PKEY_ALG_CTRL + 7,
+ EVP_PKEY_CTRL_PASS = EVP_PKEY_ALG_CTRL + 8,
+ EVP_PKEY_CTRL_SCRYPT_SALT = EVP_PKEY_ALG_CTRL + 9,
+ EVP_PKEY_CTRL_SCRYPT_N = EVP_PKEY_ALG_CTRL + 10,
+ EVP_PKEY_CTRL_SCRYPT_R = EVP_PKEY_ALG_CTRL + 11,
+ EVP_PKEY_CTRL_SCRYPT_P = EVP_PKEY_ALG_CTRL + 12,
+ EVP_PKEY_CTRL_SCRYPT_MAXMEM_BYTES = EVP_PKEY_ALG_CTRL + 13,
+}
+
+-- clean up error occurs during OBJ_txt2*
+C.ERR_clear_error()
+
+_M.ecx_curves = {
+ Ed25519 = _M.EVP_PKEY_ED25519,
+ X25519 = _M.EVP_PKEY_X25519,
+ Ed448 = _M.EVP_PKEY_ED448,
+ X448 = _M.EVP_PKEY_X448,
+}
+
+return _M
diff --git a/server/resty/openssl/include/evp/cipher.lua b/server/resty/openssl/include/evp/cipher.lua
new file mode 100644
index 0000000..c803766
--- /dev/null
+++ b/server/resty/openssl/include/evp/cipher.lua
@@ -0,0 +1,123 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+ffi.cdef [[
+ // openssl < 3.0
+ int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad);
+
+ const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx);
+ const EVP_CIPHER *EVP_get_cipherbyname(const char *name);
+ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
+ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl, const unsigned char *in, int inl);
+ int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl, const unsigned char *in, int inl);
+
+
+ int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,
+ const EVP_CIPHER *cipher, ENGINE *impl,
+ const unsigned char *key,
+ const unsigned char *iv, int enc);
+ int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl, const unsigned char *in, int inl);
+ int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,
+ int *outl);
+
+ // list functions
+ typedef void* fake_openssl_cipher_list_fn(const EVP_CIPHER *ciph, const char *from,
+ const char *to, void *x);
+ //void EVP_CIPHER_do_all_sorted(fake_openssl_cipher_list_fn*, void *arg);
+ void EVP_CIPHER_do_all_sorted(void (*fn)
+ (const EVP_CIPHER *ciph, const char *from,
+ const char *to, void *x), void *arg);
+ int EVP_CIPHER_nid(const EVP_CIPHER *cipher);
+]]
+
+if BORINGSSL then
+ ffi.cdef [[
+ int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
+ const uint8_t *salt, const uint8_t *data,
+ size_t data_len, unsigned count, uint8_t *key,
+ uint8_t *iv);
+ ]]
+else
+ ffi.cdef [[
+ int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
+ const unsigned char *salt,
+ const unsigned char *data, int datal, int count,
+ unsigned char *key, unsigned char *iv);
+ ]]
+end
+
+if OPENSSL_3X then
+ require "resty.openssl.include.provider"
+
+ ffi.cdef [[
+ int EVP_CIPHER_CTX_get_block_size(const EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_get_key_length(const EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_get_iv_length(const EVP_CIPHER_CTX *ctx);
+
+ int EVP_CIPHER_get_nid(const EVP_CIPHER *cipher);
+
+ const OSSL_PROVIDER *EVP_CIPHER_get0_provider(const EVP_CIPHER *cipher);
+ EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
+ const char *properties);
+
+ typedef void* fake_openssl_cipher_provided_list_fn(EVP_CIPHER *cipher, void *arg);
+ void EVP_CIPHER_do_all_provided(OSSL_LIB_CTX *libctx,
+ fake_openssl_cipher_provided_list_fn*,
+ void *arg);
+ int EVP_CIPHER_up_ref(EVP_CIPHER *cipher);
+ void EVP_CIPHER_free(EVP_CIPHER *cipher);
+
+ const char *EVP_CIPHER_get0_name(const EVP_CIPHER *cipher);
+
+ int EVP_CIPHER_CTX_set_params(EVP_CIPHER_CTX *ctx, const OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_CIPHER_CTX_settable_params(EVP_CIPHER_CTX *ctx);
+ int EVP_CIPHER_CTX_get_params(EVP_CIPHER_CTX *ctx, OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_CIPHER_CTX_gettable_params(EVP_CIPHER_CTX *ctx);
+ ]]
+end
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
+ int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
+ void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *c);
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);
+ int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
+
+ // # define EVP_MAX_IV_LENGTH 16
+ // # define EVP_MAX_BLOCK_LENGTH 32
+
+ struct evp_cipher_ctx_st {
+ const EVP_CIPHER *cipher;
+ ENGINE *engine; /* functional reference if 'cipher' is
+ * ENGINE-provided */
+ int encrypt; /* encrypt or decrypt */
+ int buf_len; /* number we have left */
+ unsigned char oiv[16]; /* original iv EVP_MAX_IV_LENGTH */
+ unsigned char iv[16]; /* working iv EVP_MAX_IV_LENGTH */
+ unsigned char buf[32]; /* saved partial block EVP_MAX_BLOCK_LENGTH */
+ int num; /* used by cfb/ofb/ctr mode */
+ void *app_data; /* application stuff */
+ int key_len; /* May change for variable length cipher */
+ unsigned long flags; /* Various flags */
+ void *cipher_data; /* per EVP data */
+ int final_used;
+ int block_mask;
+ unsigned char final[32]; /* possible final block EVP_MAX_BLOCK_LENGTH */
+ } /* EVP_CIPHER_CTX */ ;
+ ]]
+end \ No newline at end of file
diff --git a/server/resty/openssl/include/evp/kdf.lua b/server/resty/openssl/include/evp/kdf.lua
new file mode 100644
index 0000000..1fd408f
--- /dev/null
+++ b/server/resty/openssl/include/evp/kdf.lua
@@ -0,0 +1,148 @@
+local ffi = require "ffi"
+local ffi_cast = ffi.cast
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.evp.md"
+local evp = require("resty.openssl.include.evp")
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local void_ptr = ctypes.void_ptr
+
+local _M = {
+ EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND = 0,
+ EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY = 1,
+ EVP_PKEY_HKDEF_MODE_EXPAND_ONLY = 2,
+}
+
+if OPENSSL_3X then
+ require "resty.openssl.include.provider"
+
+ ffi.cdef [[
+ const OSSL_PROVIDER *EVP_KDF_get0_provider(const EVP_KDF *kdf);
+
+ typedef void* fake_openssl_kdf_provided_list_fn(EVP_KDF *kdf, void *arg);
+ void EVP_KDF_do_all_provided(OSSL_LIB_CTX *libctx,
+ fake_openssl_kdf_provided_list_fn*,
+ void *arg);
+ int EVP_KDF_up_ref(EVP_KDF *kdf);
+ void EVP_KDF_free(EVP_KDF *kdf);
+
+ const char *EVP_KDF_get0_name(const EVP_KDF *kdf);
+
+ EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
+ const char *properties);
+ EVP_KDF_CTX *EVP_KDF_CTX_new(const EVP_KDF *kdf);
+ void EVP_KDF_CTX_free(EVP_KDF_CTX *ctx);
+ void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx);
+
+ size_t EVP_KDF_CTX_get_kdf_size(EVP_KDF_CTX *ctx);
+ int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[]);
+
+ int EVP_KDF_CTX_get_params(EVP_KDF_CTX *ctx, OSSL_PARAM params[]);
+ int EVP_KDF_CTX_set_params(EVP_KDF_CTX *ctx, const OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_KDF_CTX_gettable_params(const EVP_KDF_CTX *ctx);
+ const OSSL_PARAM *EVP_KDF_CTX_settable_params(const EVP_KDF_CTX *ctx);
+ ]]
+end
+
+if OPENSSL_3X or BORINGSSL then
+ ffi.cdef [[
+ int EVP_PKEY_CTX_set_tls1_prf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+ int EVP_PKEY_CTX_set1_tls1_prf_secret(EVP_PKEY_CTX *pctx,
+ const unsigned char *sec, int seclen);
+ int EVP_PKEY_CTX_add1_tls1_prf_seed(EVP_PKEY_CTX *pctx,
+ const unsigned char *seed, int seedlen);
+
+ int EVP_PKEY_CTX_set_hkdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+ int EVP_PKEY_CTX_set1_hkdf_salt(EVP_PKEY_CTX *ctx,
+ const unsigned char *salt, int saltlen);
+ int EVP_PKEY_CTX_set1_hkdf_key(EVP_PKEY_CTX *ctx,
+ const unsigned char *key, int keylen);
+ int EVP_PKEY_CTX_set_hkdf_mode(EVP_PKEY_CTX *ctx, int mode);
+ int EVP_PKEY_CTX_add1_hkdf_info(EVP_PKEY_CTX *ctx,
+ const unsigned char *info, int infolen);
+ ]]
+
+ _M.EVP_PKEY_CTX_set_tls1_prf_md = function(pctx, md)
+ return C.EVP_PKEY_CTX_set_tls1_prf_md(pctx, md)
+ end
+ _M.EVP_PKEY_CTX_set1_tls1_prf_secret = function(pctx, sec)
+ return C.EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, sec, #sec)
+ end
+ _M.EVP_PKEY_CTX_add1_tls1_prf_seed = function(pctx, seed)
+ return C.EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed, #seed)
+ end
+
+ _M.EVP_PKEY_CTX_set_hkdf_md = function(pctx, md)
+ return C.EVP_PKEY_CTX_set_hkdf_md(pctx, md)
+ end
+ _M.EVP_PKEY_CTX_set1_hkdf_salt = function(pctx, salt)
+ return C.EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, #salt)
+ end
+ _M.EVP_PKEY_CTX_set1_hkdf_key = function(pctx, key)
+ return C.EVP_PKEY_CTX_set1_hkdf_key(pctx, key, #key)
+ end
+ _M.EVP_PKEY_CTX_set_hkdf_mode = function(pctx, mode)
+ return C.EVP_PKEY_CTX_set_hkdf_mode(pctx, mode)
+ end
+ _M.EVP_PKEY_CTX_add1_hkdf_info = function(pctx, info)
+ return C.EVP_PKEY_CTX_add1_hkdf_info(pctx, info, #info)
+ end
+
+else
+ _M.EVP_PKEY_CTX_set_tls1_prf_md = function(pctx, md)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_TLS_MD,
+ 0, ffi_cast(void_ptr, md))
+ end
+ _M.EVP_PKEY_CTX_set1_tls1_prf_secret = function(pctx, sec)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_TLS_SECRET,
+ #sec, ffi_cast(void_ptr, sec))
+ end
+ _M.EVP_PKEY_CTX_add1_tls1_prf_seed = function(pctx, seed)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_TLS_SEED,
+ #seed, ffi_cast(void_ptr, seed))
+ end
+
+ _M.EVP_PKEY_CTX_set_hkdf_md = function(pctx, md)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_HKDF_MD,
+ 0, ffi_cast(void_ptr, md))
+ end
+ _M.EVP_PKEY_CTX_set1_hkdf_salt = function(pctx, salt)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_HKDF_SALT,
+ #salt, ffi_cast(void_ptr, salt))
+ end
+ _M.EVP_PKEY_CTX_set1_hkdf_key = function(pctx, key)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_HKDF_KEY,
+ #key, ffi_cast(void_ptr, key))
+ end
+ _M.EVP_PKEY_CTX_set_hkdf_mode = function(pctx, mode)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_HKDF_MODE,
+ mode, nil)
+ end
+ _M.EVP_PKEY_CTX_add1_hkdf_info = function(pctx, info)
+ return C.EVP_PKEY_CTX_ctrl(pctx, -1,
+ evp.EVP_PKEY_OP_DERIVE,
+ evp.EVP_PKEY_CTRL_HKDF_INFO,
+ #info, ffi_cast(void_ptr, info))
+ end
+end
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/include/evp/mac.lua b/server/resty/openssl/include/evp/mac.lua
new file mode 100644
index 0000000..a831076
--- /dev/null
+++ b/server/resty/openssl/include/evp/mac.lua
@@ -0,0 +1,38 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.provider"
+
+ffi.cdef [[
+ typedef struct evp_mac_st EVP_MAC;
+ typedef struct evp_mac_ctx_st EVP_MAC_CTX;
+
+ EVP_MAC_CTX *EVP_MAC_CTX_new(EVP_MAC *mac);
+ void EVP_MAC_CTX_free(EVP_MAC_CTX *ctx);
+
+ const OSSL_PROVIDER *EVP_MAC_get0_provider(const EVP_MAC *mac);
+ EVP_MAC *EVP_MAC_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
+ const char *properties);
+
+ int EVP_MAC_init(EVP_MAC_CTX *ctx, const unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[]);
+ int EVP_MAC_update(EVP_MAC_CTX *ctx, const unsigned char *data, size_t datalen);
+ int EVP_MAC_final(EVP_MAC_CTX *ctx,
+ unsigned char *out, size_t *outl, size_t outsize);
+
+ size_t EVP_MAC_CTX_get_mac_size(EVP_MAC_CTX *ctx);
+
+ typedef void* fake_openssl_mac_provided_list_fn(EVP_MAC *mac, void *arg);
+ void EVP_MAC_do_all_provided(OSSL_LIB_CTX *libctx,
+ fake_openssl_mac_provided_list_fn*,
+ void *arg);
+ int EVP_MAC_up_ref(EVP_MAC *mac);
+ void EVP_MAC_free(EVP_MAC *mac);
+
+ const char *EVP_MAC_get0_name(const EVP_MAC *mac);
+
+ int EVP_MAC_CTX_set_params(EVP_MAC_CTX *ctx, const OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_MAC_CTX_settable_params(EVP_MAC_CTX *ctx);
+ int EVP_MAC_CTX_get_params(EVP_MAC_CTX *ctx, OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_MAC_CTX_gettable_params(EVP_MAC_CTX *ctx);
+]] \ No newline at end of file
diff --git a/server/resty/openssl/include/evp/md.lua b/server/resty/openssl/include/evp/md.lua
new file mode 100644
index 0000000..1794ce1
--- /dev/null
+++ b/server/resty/openssl/include/evp/md.lua
@@ -0,0 +1,86 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+ffi.cdef [[
+ int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type,
+ ENGINE *impl);
+ int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d,
+ size_t cnt);
+ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,
+ unsigned int *s);
+ const EVP_MD *EVP_get_digestbyname(const char *name);
+ int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d,
+ size_t cnt);
+ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,
+ unsigned int *s);
+
+ const EVP_MD *EVP_md_null(void);
+ // openssl < 3.0
+ int EVP_MD_size(const EVP_MD *md);
+ int EVP_MD_type(const EVP_MD *md);
+
+ typedef void* fake_openssl_md_list_fn(const EVP_MD *ciph, const char *from,
+ const char *to, void *x);
+ void EVP_MD_do_all_sorted(fake_openssl_md_list_fn*, void *arg);
+
+ const EVP_MD *EVP_get_digestbyname(const char *name);
+]]
+
+if OPENSSL_3X then
+ require "resty.openssl.include.provider"
+
+ ffi.cdef [[
+ int EVP_MD_get_size(const EVP_MD *md);
+ int EVP_MD_get_type(const EVP_MD *md);
+ const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md);
+
+ EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
+ const char *properties);
+
+ typedef void* fake_openssl_md_provided_list_fn(EVP_MD *md, void *arg);
+ void EVP_MD_do_all_provided(OSSL_LIB_CTX *libctx,
+ fake_openssl_md_provided_list_fn*,
+ void *arg);
+ int EVP_MD_up_ref(EVP_MD *md);
+ void EVP_MD_free(EVP_MD *md);
+
+ const char *EVP_MD_get0_name(const EVP_MD *md);
+
+ int EVP_MD_CTX_set_params(EVP_MD_CTX *ctx, const OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_MD_CTX_settable_params(EVP_MD_CTX *ctx);
+ int EVP_MD_CTX_get_params(EVP_MD_CTX *ctx, OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_MD_CTX_gettable_params(EVP_MD_CTX *ctx);
+ ]]
+end
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ EVP_MD_CTX *EVP_MD_CTX_new(void);
+ void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ EVP_MD_CTX *EVP_MD_CTX_create(void);
+ void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);
+
+ // crypto/evp/evp.h
+ // only needed for openssl 1.0.x where initializer for HMAC_CTX is not avaiable
+ // HACK: renamed from env_md_ctx_st to evp_md_ctx_st to match typedef (lazily)
+ // it's an internal struct thus name is not exported so we will be fine
+ struct evp_md_ctx_st {
+ const EVP_MD *digest;
+ ENGINE *engine; /* functional reference if 'digest' is
+ * ENGINE-provided */
+ unsigned long flags;
+ void *md_data;
+ /* Public key context for sign/verify */
+ EVP_PKEY_CTX *pctx;
+ /* Update function: usually copied from EVP_MD */
+ int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
+ } /* EVP_MD_CTX */ ;
+ ]]
+end \ No newline at end of file
diff --git a/server/resty/openssl/include/evp/pkey.lua b/server/resty/openssl/include/evp/pkey.lua
new file mode 100644
index 0000000..ee1a213
--- /dev/null
+++ b/server/resty/openssl/include/evp/pkey.lua
@@ -0,0 +1,234 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.evp.md"
+local evp = require("resty.openssl.include.evp")
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+ffi.cdef [[
+ EVP_PKEY *EVP_PKEY_new(void);
+ void EVP_PKEY_free(EVP_PKEY *pkey);
+
+ RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey);
+ EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey);
+ DH *EVP_PKEY_get0_DH(EVP_PKEY *pkey);
+
+ int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
+ // openssl < 3.0
+ int EVP_PKEY_base_id(const EVP_PKEY *pkey);
+ int EVP_PKEY_size(const EVP_PKEY *pkey);
+
+ EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
+ EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e);
+ void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype,
+ int cmd, int p1, void *p2);
+ // TODO replace EVP_PKEY_CTX_ctrl with EVP_PKEY_CTX_ctrl_str to reduce
+ // some hardcoded macros
+ int EVP_PKEY_CTX_ctrl_str(EVP_PKEY_CTX *ctx, const char *type,
+ const char *value);
+ int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx,
+ unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen);
+ int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,
+ unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen);
+
+ int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_sign(EVP_PKEY_CTX *ctx,
+ unsigned char *sig, size_t *siglen,
+ const unsigned char *tbs, size_t tbslen);
+ int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_verify_recover(EVP_PKEY_CTX *ctx,
+ unsigned char *rout, size_t *routlen,
+ const unsigned char *sig, size_t siglen);
+
+ EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e,
+ const unsigned char *key, size_t keylen);
+ EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e,
+ const unsigned char *key, size_t keylen);
+
+ int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
+ size_t *len);
+ int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
+ size_t *len);
+
+ int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s,
+ EVP_PKEY *pkey);
+ int EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf,
+ unsigned int siglen, EVP_PKEY *pkey);
+
+ int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+ const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey);
+ int EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret,
+ size_t *siglen, const unsigned char *tbs,
+ size_t tbslen);
+ int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
+ const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey);
+ int EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret,
+ size_t siglen, const unsigned char *tbs, size_t tbslen);
+
+ int EVP_PKEY_get_default_digest_nid(EVP_PKEY *pkey, int *pnid);
+
+ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
+ int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
+
+ int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
+ int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
+]]
+
+if OPENSSL_3X then
+ require "resty.openssl.include.provider"
+
+ ffi.cdef [[
+ int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad_mode);
+
+ int EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
+ int EVP_PKEY_get_size(const EVP_PKEY *pkey);
+
+ const OSSL_PROVIDER *EVP_PKEY_get0_provider(const EVP_PKEY *key);
+ const OSSL_PROVIDER *EVP_PKEY_CTX_get0_provider(const EVP_PKEY_CTX *ctx);
+
+ const OSSL_PARAM *EVP_PKEY_settable_params(const EVP_PKEY *pkey);
+ int EVP_PKEY_set_params(EVP_PKEY *pkey, OSSL_PARAM params[]);
+ int EVP_PKEY_get_params(EVP_PKEY *ctx, OSSL_PARAM params[]);
+ const OSSL_PARAM *EVP_PKEY_gettable_params(EVP_PKEY *ctx);
+ ]]
+end
+
+if OPENSSL_10 then
+ ffi.cdef [[
+ // crypto/evp/evp.h
+ // only needed for openssl 1.0.x where getters are not available
+ // needed to get key to extract parameters
+ // Note: this struct is trimmed
+ struct evp_pkey_st {
+ int type;
+ int save_type;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ ENGINE *engine;
+ ENGINE *pmeth_engine;
+ union {
+ void *ptr;
+ struct rsa_st *rsa;
+ struct dsa_st *dsa;
+ struct dh_st *dh;
+ struct ec_key_st *ec;
+ } pkey;
+ // trimmed
+
+ // CRYPTO_REF_COUNT references;
+ // CRYPTO_RWLOCK *lock;
+ // STACK_OF(X509_ATTRIBUTE) *attributes;
+ // int save_parameters;
+
+ // struct {
+ // EVP_KEYMGMT *keymgmt;
+ // void *provkey;
+ // } pkeys[10];
+ // size_t dirty_cnt_copy;
+ };
+ ]]
+end
+
+local _M = {}
+
+if OPENSSL_3X or BORINGSSL then
+ ffi.cdef [[
+ int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
+ int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int param_enc);
+
+ int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int mbits);
+ int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp);
+
+ int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
+ int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int len);
+
+ int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits);
+ ]]
+ _M.EVP_PKEY_CTX_set_ec_paramgen_curve_nid = function(pctx, nid)
+ return C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid)
+ end
+ _M.EVP_PKEY_CTX_set_ec_param_enc = function(pctx, param_enc)
+ return C.EVP_PKEY_CTX_set_ec_param_enc(pctx, param_enc)
+ end
+
+ _M.EVP_PKEY_CTX_set_rsa_keygen_bits = function(pctx, mbits)
+ return C.EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, mbits)
+ end
+ _M.EVP_PKEY_CTX_set_rsa_keygen_pubexp = function(pctx, pubexp)
+ return C.EVP_PKEY_CTX_set_rsa_keygen_pubexp(pctx, pubexp)
+ end
+
+ _M.EVP_PKEY_CTX_set_rsa_padding = function(pctx, pad)
+ return C.EVP_PKEY_CTX_set_rsa_padding(pctx, pad)
+ end
+ _M.EVP_PKEY_CTX_set_rsa_pss_saltlen = function(pctx, len)
+ return C.EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, len)
+ end
+ _M.EVP_PKEY_CTX_set_dh_paramgen_prime_len = function(pctx, pbits)
+ return C.EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx, pbits)
+ end
+
+else
+ _M.EVP_PKEY_CTX_set_ec_paramgen_curve_nid = function(pctx, nid)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_EC,
+ evp.EVP_PKEY_OP_PARAMGEN + evp.EVP_PKEY_OP_KEYGEN,
+ evp.EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
+ nid, nil)
+ end
+ _M.EVP_PKEY_CTX_set_ec_param_enc = function(pctx, param_enc)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_EC,
+ evp.EVP_PKEY_OP_PARAMGEN + evp.EVP_PKEY_OP_KEYGEN,
+ evp.EVP_PKEY_CTRL_EC_PARAM_ENC,
+ param_enc, nil)
+ end
+
+ _M.EVP_PKEY_CTX_set_rsa_keygen_bits = function(pctx, mbits)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_RSA,
+ evp.EVP_PKEY_OP_KEYGEN,
+ evp.EVP_PKEY_CTRL_RSA_KEYGEN_BITS,
+ mbits, nil)
+ end
+ _M.EVP_PKEY_CTX_set_rsa_keygen_pubexp = function(pctx, pubexp)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_RSA, evp.EVP_PKEY_OP_KEYGEN,
+ evp.EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP,
+ 0, pubexp)
+ end
+
+ _M.EVP_PKEY_CTX_set_rsa_padding = function(pctx, pad)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_RSA,
+ -1,
+ evp.EVP_PKEY_CTRL_RSA_PADDING,
+ pad, nil)
+ end
+ _M.EVP_PKEY_CTX_set_rsa_pss_saltlen = function(pctx, len)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_RSA,
+ evp.EVP_PKEY_OP_SIGN + evp.EVP_PKEY_OP_VERIFY,
+ evp.EVP_PKEY_CTRL_RSA_PSS_SALTLEN,
+ len, nil)
+ end
+
+ _M.EVP_PKEY_CTX_set_dh_paramgen_prime_len = function(pctx, pbits)
+ return C.EVP_PKEY_CTX_ctrl(pctx,
+ evp.EVP_PKEY_DH, evp.EVP_PKEY_OP_PARAMGEN,
+ evp.EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN,
+ pbits, nil)
+ end
+end
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/include/hmac.lua b/server/resty/openssl/include/hmac.lua
new file mode 100644
index 0000000..e08f031
--- /dev/null
+++ b/server/resty/openssl/include/hmac.lua
@@ -0,0 +1,48 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.evp"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+if BORINGSSL then
+ ffi.cdef [[
+ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
+ const EVP_MD *md, ENGINE *impl);
+ ]]
+else
+ ffi.cdef [[
+ int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len,
+ const EVP_MD *md, ENGINE *impl);
+ ]]
+end
+
+ffi.cdef [[
+ int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data,
+ size_t len);
+ int HMAC_Final(HMAC_CTX *ctx, unsigned char *md,
+ unsigned int *len);
+]]
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ HMAC_CTX *HMAC_CTX_new(void);
+ void HMAC_CTX_free(HMAC_CTX *ctx);
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ // # define HMAC_MAX_MD_CBLOCK 128/* largest known is SHA512 */
+ struct hmac_ctx_st {
+ const EVP_MD *md;
+ EVP_MD_CTX md_ctx;
+ EVP_MD_CTX i_ctx;
+ EVP_MD_CTX o_ctx;
+ unsigned int key_length;
+ unsigned char key[128];
+ };
+
+ void HMAC_CTX_init(HMAC_CTX *ctx);
+ void HMAC_CTX_cleanup(HMAC_CTX *ctx);
+ ]]
+end \ No newline at end of file
diff --git a/server/resty/openssl/include/objects.lua b/server/resty/openssl/include/objects.lua
new file mode 100644
index 0000000..aecd324
--- /dev/null
+++ b/server/resty/openssl/include/objects.lua
@@ -0,0 +1,19 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name);
+ ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name);
+ int OBJ_txt2nid(const char *s);
+ const char *OBJ_nid2sn(int n);
+ int OBJ_ln2nid(const char *s);
+ int OBJ_sn2nid(const char *s);
+ const char *OBJ_nid2ln(int n);
+ const char *OBJ_nid2sn(int n);
+ int OBJ_obj2nid(const ASN1_OBJECT *o);
+ const ASN1_OBJECT *OBJ_nid2obj(int n);
+ int OBJ_create(const char *oid, const char *sn, const char *ln);
+
+ int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid);
+]]
diff --git a/server/resty/openssl/include/ossl_typ.lua b/server/resty/openssl/include/ossl_typ.lua
new file mode 100644
index 0000000..198c889
--- /dev/null
+++ b/server/resty/openssl/include/ossl_typ.lua
@@ -0,0 +1,71 @@
+local ffi = require "ffi"
+
+ffi.cdef(
+[[
+ typedef struct rsa_st RSA;
+ typedef struct evp_pkey_st EVP_PKEY;
+ typedef struct bignum_st BIGNUM;
+ typedef struct bn_gencb_st BN_GENCB;
+ typedef struct bignum_ctx BN_CTX;
+ typedef struct bio_st BIO;
+ typedef struct evp_cipher_st EVP_CIPHER;
+ typedef struct evp_md_ctx_st EVP_MD_CTX;
+ typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
+ typedef struct evp_md_st EVP_MD;
+ typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
+ typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
+ typedef struct engine_st ENGINE;
+ typedef struct x509_st X509;
+ typedef struct x509_attributes_st X509_ATTRIBUTE;
+ typedef struct X509_extension_st X509_EXTENSION;
+ typedef struct X509_name_st X509_NAME;
+ typedef struct X509_name_entry_st X509_NAME_ENTRY;
+ typedef struct X509_req_st X509_REQ;
+ typedef struct X509_crl_st X509_CRL;
+ typedef struct x509_store_st X509_STORE;
+ typedef struct x509_store_ctx_st X509_STORE_CTX;
+ typedef struct x509_purpose_st X509_PURPOSE;
+ typedef struct v3_ext_ctx X509V3_CTX;
+ typedef struct asn1_string_st ASN1_INTEGER;
+ typedef struct asn1_string_st ASN1_ENUMERATED;
+ typedef struct asn1_string_st ASN1_BIT_STRING;
+ typedef struct asn1_string_st ASN1_OCTET_STRING;
+ typedef struct asn1_string_st ASN1_PRINTABLESTRING;
+ typedef struct asn1_string_st ASN1_T61STRING;
+ typedef struct asn1_string_st ASN1_IA5STRING;
+ typedef struct asn1_string_st ASN1_GENERALSTRING;
+ typedef struct asn1_string_st ASN1_UNIVERSALSTRING;
+ typedef struct asn1_string_st ASN1_BMPSTRING;
+ typedef struct asn1_string_st ASN1_UTCTIME;
+ typedef struct asn1_string_st ASN1_TIME;
+ typedef struct asn1_string_st ASN1_GENERALIZEDTIME;
+ typedef struct asn1_string_st ASN1_VISIBLESTRING;
+ typedef struct asn1_string_st ASN1_UTF8STRING;
+ typedef struct asn1_string_st ASN1_STRING;
+ typedef struct asn1_object_st ASN1_OBJECT;
+ typedef struct conf_st CONF;
+ typedef struct conf_method_st CONF_METHOD;
+ typedef int ASN1_BOOLEAN;
+ typedef int ASN1_NULL;
+ typedef struct ec_key_st EC_KEY;
+ typedef struct ec_method_st EC_METHOD;
+ typedef struct ec_point_st EC_POINT;
+ typedef struct ec_group_st EC_GROUP;
+ typedef struct rsa_meth_st RSA_METHOD;
+ // typedef struct evp_keymgmt_st EVP_KEYMGMT;
+ // typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
+ // typedef struct bn_mont_ctx_st BN_MONT_CTX;
+ // typedef struct bn_blinding_st BN_BLINDING;
+ // crypto.h
+ // typedef void CRYPTO_RWLOCK;
+ typedef struct hmac_ctx_st HMAC_CTX;
+ typedef struct x509_revoked_st X509_REVOKED;
+ typedef struct dh_st DH;
+ typedef struct PKCS12_st PKCS12;
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
+ typedef struct evp_kdf_st EVP_KDF;
+ typedef struct evp_kdf_ctx_st EVP_KDF_CTX;
+ typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
+]])
+
diff --git a/server/resty/openssl/include/param.lua b/server/resty/openssl/include/param.lua
new file mode 100644
index 0000000..9c7a2e9
--- /dev/null
+++ b/server/resty/openssl/include/param.lua
@@ -0,0 +1,71 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ typedef struct ossl_param_st {
+ const char *key; /* the name of the parameter */
+ unsigned int data_type; /* declare what kind of content is in buffer */
+ void *data; /* value being passed in or out */
+ size_t data_size; /* data size */
+ size_t return_size; /* returned content size */
+ } OSSL_PARAM;
+
+ OSSL_PARAM OSSL_PARAM_construct_int(const char *key, int *buf);
+ OSSL_PARAM OSSL_PARAM_construct_uint(const char *key, unsigned int *buf);
+ OSSL_PARAM OSSL_PARAM_construct_BN(const char *key, unsigned char *buf,
+ size_t bsize);
+ OSSL_PARAM OSSL_PARAM_construct_double(const char *key, double *buf);
+ OSSL_PARAM OSSL_PARAM_construct_utf8_string(const char *key, char *buf,
+ size_t bsize);
+ OSSL_PARAM OSSL_PARAM_construct_octet_string(const char *key, void *buf,
+ size_t bsize);
+ OSSL_PARAM OSSL_PARAM_construct_utf8_ptr(const char *key, char **buf,
+ size_t bsize);
+ OSSL_PARAM OSSL_PARAM_construct_octet_ptr(const char *key, void **buf,
+ size_t bsize);
+ OSSL_PARAM OSSL_PARAM_construct_end(void);
+
+ int OSSL_PARAM_get_int32(const OSSL_PARAM *p, int32_t *val);
+ int OSSL_PARAM_get_uint32(const OSSL_PARAM *p, uint32_t *val);
+ int OSSL_PARAM_get_int64(const OSSL_PARAM *p, int64_t *val);
+ int OSSL_PARAM_get_uint64(const OSSL_PARAM *p, uint64_t *val);
+ // int OSSL_PARAM_get_size_t(const OSSL_PARAM *p, size_t *val);
+ // int OSSL_PARAM_get_time_t(const OSSL_PARAM *p, time_t *val);
+
+ int OSSL_PARAM_set_int(OSSL_PARAM *p, int val);
+ int OSSL_PARAM_set_uint(OSSL_PARAM *p, unsigned int val);
+ int OSSL_PARAM_set_long(OSSL_PARAM *p, long int val);
+ int OSSL_PARAM_set_ulong(OSSL_PARAM *p, unsigned long int val);
+ int OSSL_PARAM_set_int32(OSSL_PARAM *p, int32_t val);
+ int OSSL_PARAM_set_uint32(OSSL_PARAM *p, uint32_t val);
+ int OSSL_PARAM_set_int64(OSSL_PARAM *p, int64_t val);
+ int OSSL_PARAM_set_uint64(OSSL_PARAM *p, uint64_t val);
+ // int OSSL_PARAM_set_size_t(OSSL_PARAM *p, size_t val);
+ // int OSSL_PARAM_set_time_t(OSSL_PARAM *p, time_t val);
+
+ int OSSL_PARAM_get_double(const OSSL_PARAM *p, double *val);
+ int OSSL_PARAM_set_double(OSSL_PARAM *p, double val);
+
+ int OSSL_PARAM_get_BN(const OSSL_PARAM *p, BIGNUM **val);
+ int OSSL_PARAM_set_BN(OSSL_PARAM *p, const BIGNUM *val);
+
+ int OSSL_PARAM_get_utf8_string(const OSSL_PARAM *p, char **val, size_t max_len);
+ int OSSL_PARAM_set_utf8_string(OSSL_PARAM *p, const char *val);
+
+ int OSSL_PARAM_get_octet_string(const OSSL_PARAM *p, void **val, size_t max_len,
+ size_t *used_len);
+ int OSSL_PARAM_set_octet_string(OSSL_PARAM *p, const void *val, size_t len);
+
+ int OSSL_PARAM_get_utf8_ptr(const OSSL_PARAM *p, const char **val);
+ int OSSL_PARAM_set_utf8_ptr(OSSL_PARAM *p, const char *val);
+
+ int OSSL_PARAM_get_octet_ptr(const OSSL_PARAM *p, const void **val,
+ size_t *used_len);
+ int OSSL_PARAM_set_octet_ptr(OSSL_PARAM *p, const void *val,
+ size_t used_len);
+
+ int OSSL_PARAM_get_utf8_string_ptr(const OSSL_PARAM *p, const char **val);
+ int OSSL_PARAM_get_octet_string_ptr(const OSSL_PARAM *p, const void **val,
+ size_t *used_len);
+]]
diff --git a/server/resty/openssl/include/pem.lua b/server/resty/openssl/include/pem.lua
new file mode 100644
index 0000000..50185e5
--- /dev/null
+++ b/server/resty/openssl/include/pem.lua
@@ -0,0 +1,50 @@
+
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+
+ffi.cdef [[
+ // all pem_password_cb* has been modified to pem_password_cb to avoid a table overflow issue
+ typedef int (*pem_password_cb)(char *buf, int size, int rwflag, void *userdata);
+ EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x,
+ // the following signature has been modified to avoid ffi.cast
+ pem_password_cb cb, const char *u);
+ // pem_password_cb *cb, void *u);
+ EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x,
+ // the following signature has been modified to avoid ffi.cast
+ pem_password_cb cb, const char *u);
+ // pem_password_cb *cb, void *u);
+ int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
+ unsigned char *kstr, int klen,
+ pem_password_cb *cb, void *u);
+ int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x);
+
+ RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x,
+ // the following signature has been modified to avoid ffi.cast
+ pem_password_cb cb, const char *u);
+ // pem_password_cb *cb, void *u);
+ RSA *PEM_read_bio_RSAPublicKey(BIO *bp, RSA **x,
+ // the following signature has been modified to avoid ffi.cast
+ pem_password_cb cb, const char *u);
+ // pem_password_cb *cb, void *u);
+ int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,
+ unsigned char *kstr, int klen,
+ pem_password_cb *cb, void *u);
+ int PEM_write_bio_RSAPublicKey(BIO *bp, RSA *x);
+
+ X509_REQ *PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x, pem_password_cb cb, void *u);
+ int PEM_write_bio_X509_REQ(BIO *bp, X509_REQ *x);
+
+ X509_CRL *PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x, pem_password_cb cb, void *u);
+ int PEM_write_bio_X509_CRL(BIO *bp, X509_CRL *x);
+
+ X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb cb, void *u);
+ int PEM_write_bio_X509(BIO *bp, X509 *x);
+
+ DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb cb, void *u);
+ int PEM_write_bio_DHparams(BIO *bp, DH *x);
+
+ EC_GROUP *PEM_read_bio_ECPKParameters(BIO *bp, EC_GROUP **x, pem_password_cb cb, void *u);
+ int PEM_write_bio_ECPKParameters(BIO *bp, const EC_GROUP *x);
+
+]]
diff --git a/server/resty/openssl/include/pkcs12.lua b/server/resty/openssl/include/pkcs12.lua
new file mode 100644
index 0000000..fb74025
--- /dev/null
+++ b/server/resty/openssl/include/pkcs12.lua
@@ -0,0 +1,31 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.stack"
+
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+ffi.cdef [[
+ // hack by changing char* to const char* here
+ PKCS12 *PKCS12_create(const char *pass, const char *name, EVP_PKEY *pkey, X509 *cert,
+ OPENSSL_STACK *ca, // STACK_OF(X509)
+ int nid_key, int nid_cert, int iter, int mac_iter, int keytype);
+
+ int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
+ OPENSSL_STACK **ca); // STACK_OF(X509) **ca);
+
+ void PKCS12_free(PKCS12 *p12);
+ int i2d_PKCS12_bio(BIO *bp, PKCS12 *a);
+ PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **a);
+]]
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ PKCS12 *PKCS12_create_ex(const char *pass, const char *name, EVP_PKEY *pkey,
+ X509 *cert,
+ OPENSSL_STACK *ca, // STACK_OF(X509)
+ int nid_key, int nid_cert,
+ int iter, int mac_iter, int keytype,
+ OSSL_LIB_CTX *ctx, const char *propq);
+ ]]
+end
diff --git a/server/resty/openssl/include/provider.lua b/server/resty/openssl/include/provider.lua
new file mode 100644
index 0000000..a2bb472
--- /dev/null
+++ b/server/resty/openssl/include/provider.lua
@@ -0,0 +1,27 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.param"
+
+ffi.cdef [[
+ typedef struct ossl_provider_st OSSL_PROVIDER;
+ typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
+
+ void OSSL_PROVIDER_set_default_search_path(OSSL_LIB_CTX *libctx,
+ const char *path);
+
+
+ OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *libctx, const char *name);
+ OSSL_PROVIDER *OSSL_PROVIDER_try_load(OSSL_LIB_CTX *libctx, const char *name);
+ int OSSL_PROVIDER_unload(OSSL_PROVIDER *prov);
+ int OSSL_PROVIDER_available(OSSL_LIB_CTX *libctx, const char *name);
+
+ const OSSL_PARAM *OSSL_PROVIDER_gettable_params(OSSL_PROVIDER *prov);
+ int OSSL_PROVIDER_get_params(OSSL_PROVIDER *prov, OSSL_PARAM params[]);
+
+ // int OSSL_PROVIDER_add_builtin(OSSL_LIB_CTX *libctx, const char *name,
+ // ossl_provider_init_fn *init_fn);
+
+ const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov);
+ int OSSL_PROVIDER_self_test(const OSSL_PROVIDER *prov);
+]]
diff --git a/server/resty/openssl/include/rand.lua b/server/resty/openssl/include/rand.lua
new file mode 100644
index 0000000..90f44c1
--- /dev/null
+++ b/server/resty/openssl/include/rand.lua
@@ -0,0 +1,24 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+if BORINGSSL then
+ ffi.cdef [[
+ int RAND_bytes(uint8_t *buf, size_t num);
+ int RAND_priv_bytes(uint8_t *buf, size_t num);
+ ]]
+elseif OPENSSL_3X then
+ ffi.cdef [[
+ int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
+ unsigned int strength);
+ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
+ unsigned int strength);
+ ]]
+else
+ ffi.cdef [[
+ int RAND_bytes(unsigned char *buf, int num);
+ int RAND_priv_bytes(unsigned char *buf, int num);
+ ]]
+end
diff --git a/server/resty/openssl/include/rsa.lua b/server/resty/openssl/include/rsa.lua
new file mode 100644
index 0000000..d7de5f4
--- /dev/null
+++ b/server/resty/openssl/include/rsa.lua
@@ -0,0 +1,70 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+
+ffi.cdef [[
+ RSA *RSA_new(void);
+ void RSA_free(RSA *r);
+]]
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ void RSA_get0_key(const RSA *r,
+ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d);
+ void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q);
+ void RSA_get0_crt_params(const RSA *r,
+ const BIGNUM **dmp1, const BIGNUM **dmq1,
+ const BIGNUM **iqmp);
+
+ int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
+ int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q);
+ int RSA_set0_crt_params(RSA *r,BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp);
+ struct rsa_st;
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ // crypto/rsa/rsa_locl.h
+ // needed to extract parameters
+ // Note: this struct is trimmed
+ struct rsa_st {
+ int pad;
+ // the following has been changed in OpenSSL 1.1.x to int32_t
+ long version;
+ const RSA_METHOD *meth;
+ ENGINE *engine;
+ BIGNUM *n;
+ BIGNUM *e;
+ BIGNUM *d;
+ BIGNUM *p;
+ BIGNUM *q;
+ BIGNUM *dmp1;
+ BIGNUM *dmq1;
+ BIGNUM *iqmp;
+ // trimmed
+
+ // CRYPTO_EX_DATA ex_data;
+ // int references;
+ // int flags;
+ // BN_MONT_CTX *_method_mod_n;
+ // BN_MONT_CTX *_method_mod_p;
+ // BN_MONT_CTX *_method_mod_q;
+
+ // char *bignum_data;
+ // BN_BLINDING *blinding;
+ // BN_BLINDING *mt_blinding;
+ };
+ ]]
+end
+
+return {
+ paddings = {
+ RSA_PKCS1_PADDING = 1,
+ RSA_SSLV23_PADDING = 2,
+ RSA_NO_PADDING = 3,
+ RSA_PKCS1_OAEP_PADDING = 4,
+ RSA_X931_PADDING = 5,
+ RSA_PKCS1_PSS_PADDING = 6,
+ },
+}
diff --git a/server/resty/openssl/include/ssl.lua b/server/resty/openssl/include/ssl.lua
new file mode 100644
index 0000000..1219ac3
--- /dev/null
+++ b/server/resty/openssl/include/ssl.lua
@@ -0,0 +1,113 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.stack"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+ffi.cdef [[
+ // SSL_METHOD
+ typedef struct ssl_method_st SSL_METHOD;
+ const SSL_METHOD *TLS_method(void);
+ const SSL_METHOD *TLS_server_method(void);
+
+ // SSL_CIPHER
+ typedef struct ssl_cipher_st SSL_CIPHER;
+ const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher);
+ SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl);
+
+ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
+ void SSL_CTX_free(SSL_CTX *a);
+
+ // SSL_SESSION
+ typedef struct ssl_session_st SSL_SESSION;
+ SSL_SESSION *SSL_get_session(const SSL *ssl);
+ long SSL_SESSION_set_timeout(SSL_SESSION *s, long t);
+ long SSL_SESSION_get_timeout(const SSL_SESSION *s);
+
+ typedef int (*SSL_CTX_alpn_select_cb_func)(SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg);
+ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
+ SSL_CTX_alpn_select_cb_func cb,
+ void *arg);
+
+ int SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
+ const unsigned char *server,
+ unsigned int server_len,
+ const unsigned char *client,
+ unsigned int client_len);
+
+ SSL *SSL_new(SSL_CTX *ctx);
+ void SSL_free(SSL *ssl);
+
+ int SSL_set_cipher_list(SSL *ssl, const char *str);
+ int SSL_set_ciphersuites(SSL *s, const char *str);
+
+ long SSL_set_options(SSL *ssl, long options);
+ long SSL_clear_options(SSL *ssl, long options);
+ long SSL_get_options(SSL *ssl);
+
+ /*STACK_OF(SSL_CIPHER)*/ OPENSSL_STACK *SSL_get_ciphers(const SSL *ssl);
+ /*STACK_OF(SSL_CIPHER)*/ OPENSSL_STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx);
+ OPENSSL_STACK *SSL_get_peer_cert_chain(const SSL *ssl);
+
+ typedef int (*verify_callback)(int preverify_ok, X509_STORE_CTX *x509_ctx);
+ void SSL_set_verify(SSL *s, int mode,
+ int (*verify_callback)(int, X509_STORE_CTX *));
+
+ int SSL_add_client_CA(SSL *ssl, X509 *cacert);
+
+ long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg);
+]]
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ X509 *SSL_get1_peer_certificate(const SSL *ssl);
+ ]]
+else
+ ffi.cdef [[
+ X509 *SSL_get_peer_certificate(const SSL *ssl);
+ ]]
+end
+
+if BORINGSSL then
+ ffi.cdef [[
+ int SSL_set_min_proto_version(SSL *ssl, int version);
+ int SSL_set_max_proto_version(SSL *ssl, int version);
+ ]]
+end
+
+local SSL_CTRL_SET_MIN_PROTO_VERSION = 123
+local SSL_CTRL_SET_MAX_PROTO_VERSION = 124
+
+local SSL_set_min_proto_version
+if BORINGSSL then
+ SSL_set_min_proto_version = function(ctx, version)
+ return C.SSL_set_min_proto_version(ctx, version)
+ end
+else
+ SSL_set_min_proto_version = function(ctx, version)
+ return C.SSL_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nil)
+ end
+end
+
+local SSL_set_max_proto_version
+if BORINGSSL then
+ SSL_set_max_proto_version = function(ctx, version)
+ return C.SSL_set_max_proto_version(ctx, version)
+ end
+else
+ SSL_set_max_proto_version = function(ctx, version)
+ return C.SSL_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nil)
+ end
+end
+
+return {
+ SSL_set_min_proto_version = SSL_set_min_proto_version,
+ SSL_set_max_proto_version = SSL_set_max_proto_version,
+}
diff --git a/server/resty/openssl/include/stack.lua b/server/resty/openssl/include/stack.lua
new file mode 100644
index 0000000..5732608
--- /dev/null
+++ b/server/resty/openssl/include/stack.lua
@@ -0,0 +1,95 @@
+--[[
+ The OpenSSL stack library. Note `safestack` is not usable here in ffi because
+ those symbols are eaten after preprocessing.
+ Instead, we should do a Lua land type checking by having a nested field indicating
+ which type of cdata its ctx holds.
+]]
+
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local _M = {}
+
+ffi.cdef [[
+ typedef char *OPENSSL_STRING;
+]]
+
+if OPENSSL_11_OR_LATER and not BORINGSSL then
+ ffi.cdef [[
+ typedef struct stack_st OPENSSL_STACK;
+
+ OPENSSL_STACK *OPENSSL_sk_new_null(void);
+ int OPENSSL_sk_push(OPENSSL_STACK *st, const void *data);
+ void OPENSSL_sk_pop_free(OPENSSL_STACK *st, void (*func) (void *));
+ int OPENSSL_sk_num(const OPENSSL_STACK *);
+ void *OPENSSL_sk_value(const OPENSSL_STACK *, int);
+ OPENSSL_STACK *OPENSSL_sk_dup(const OPENSSL_STACK *st);
+ void OPENSSL_sk_free(OPENSSL_STACK *);
+ void *OPENSSL_sk_delete(OPENSSL_STACK *st, int loc);
+
+ typedef void (*OPENSSL_sk_freefunc)(void *);
+ typedef void *(*OPENSSL_sk_copyfunc)(const void *);
+ OPENSSL_STACK *OPENSSL_sk_deep_copy(const OPENSSL_STACK *,
+ OPENSSL_sk_copyfunc c,
+ OPENSSL_sk_freefunc f);
+ ]]
+ _M.OPENSSL_sk_pop_free = C.OPENSSL_sk_pop_free
+
+ _M.OPENSSL_sk_new_null = C.OPENSSL_sk_new_null
+ _M.OPENSSL_sk_push = C.OPENSSL_sk_push
+ _M.OPENSSL_sk_pop_free = C.OPENSSL_sk_pop_free
+ _M.OPENSSL_sk_num = C.OPENSSL_sk_num
+ _M.OPENSSL_sk_value = C.OPENSSL_sk_value
+ _M.OPENSSL_sk_dup = C.OPENSSL_sk_dup
+ _M.OPENSSL_sk_delete = C.OPENSSL_sk_delete
+ _M.OPENSSL_sk_free = C.OPENSSL_sk_free
+ _M.OPENSSL_sk_deep_copy = C.OPENSSL_sk_deep_copy
+elseif OPENSSL_10 or BORINGSSL then
+ ffi.cdef [[
+ typedef struct stack_st _STACK;
+ // i made this up
+ typedef struct stack_st OPENSSL_STACK;
+
+ _STACK *sk_new_null(void);
+ void sk_pop_free(_STACK *st, void (*func) (void *));
+ _STACK *sk_dup(_STACK *st);
+ void sk_free(_STACK *st);
+
+ _STACK *sk_deep_copy(_STACK *, void *(*)(void *), void (*)(void *));
+ ]]
+
+ if BORINGSSL then -- indices are using size_t instead of int
+ ffi.cdef [[
+ size_t sk_push(_STACK *st, void *data);
+ size_t sk_num(const _STACK *);
+ void *sk_value(const _STACK *, size_t);
+ void *sk_delete(_STACK *st, size_t loc);
+ ]]
+ else -- normal OpenSSL 1.0
+ ffi.cdef [[
+ int sk_push(_STACK *st, void *data);
+ int sk_num(const _STACK *);
+ void *sk_value(const _STACK *, int);
+ void *sk_delete(_STACK *st, int loc);
+ ]]
+ end
+
+ _M.OPENSSL_sk_pop_free = C.sk_pop_free
+
+ _M.OPENSSL_sk_new_null = C.sk_new_null
+ _M.OPENSSL_sk_push = function(...) return tonumber(C.sk_push(...)) end
+ _M.OPENSSL_sk_pop_free = C.sk_pop_free
+ _M.OPENSSL_sk_num = function(...) return tonumber(C.sk_num(...)) end
+ _M.OPENSSL_sk_value = C.sk_value
+ _M.OPENSSL_sk_delete = C.sk_delete
+ _M.OPENSSL_sk_dup = C.sk_dup
+ _M.OPENSSL_sk_free = C.sk_free
+ _M.OPENSSL_sk_deep_copy = C.sk_deep_copy
+end
+
+return _M
diff --git a/server/resty/openssl/include/x509/altname.lua b/server/resty/openssl/include/x509/altname.lua
new file mode 100644
index 0000000..ce1db67
--- /dev/null
+++ b/server/resty/openssl/include/x509/altname.lua
@@ -0,0 +1,49 @@
+local GEN_OTHERNAME = 0
+local GEN_EMAIL = 1
+local GEN_DNS = 2
+local GEN_X400 = 3
+local GEN_DIRNAME = 4
+local GEN_EDIPARTY = 5
+local GEN_URI = 6
+local GEN_IPADD = 7
+local GEN_RID = 8
+
+local default_types = {
+ OtherName = GEN_OTHERNAME, -- otherName
+ RFC822Name = GEN_EMAIL, -- email
+ RFC822 = GEN_EMAIL,
+ Email = GEN_EMAIL,
+ DNSName = GEN_DNS, -- dns
+ DNS = GEN_DNS,
+ X400 = GEN_X400, -- x400
+ DirName = GEN_DIRNAME, -- dirName
+ EdiParty = GEN_EDIPARTY, -- EdiParty
+ UniformResourceIdentifier = GEN_URI, -- uri
+ URI = GEN_URI,
+ IPAddress = GEN_IPADD, -- ipaddr
+ IP = GEN_IPADD,
+ RID = GEN_RID, -- rid
+}
+
+local literals = {
+ [GEN_OTHERNAME] = "OtherName",
+ [GEN_EMAIL] = "email",
+ [GEN_DNS] = "DNS",
+ [GEN_X400] = "X400",
+ [GEN_DIRNAME] = "DirName",
+ [GEN_EDIPARTY] = "EdiParty",
+ [GEN_URI] = "URI",
+ [GEN_IPADD] = "IP",
+ [GEN_RID] = "RID",
+}
+
+local types = {}
+for t, gid in pairs(default_types) do
+ types[t:lower()] = gid
+ types[t] = gid
+end
+
+return {
+ types = types,
+ literals = literals,
+} \ No newline at end of file
diff --git a/server/resty/openssl/include/x509/crl.lua b/server/resty/openssl/include/x509/crl.lua
new file mode 100644
index 0000000..7870cd3
--- /dev/null
+++ b/server/resty/openssl/include/x509/crl.lua
@@ -0,0 +1,86 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.evp"
+require "resty.openssl.include.objects"
+require "resty.openssl.include.x509"
+require "resty.openssl.include.stack"
+
+local asn1_macro = require "resty.openssl.include.asn1"
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
+
+asn1_macro.declare_asn1_functions("X509_CRL", asn1_macro.has_new_ex)
+
+ffi.cdef [[
+ X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl);
+ int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name);
+ int X509_CRL_set_version(X509_CRL *x, long version);
+
+ int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
+ X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc);
+ int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid, int lastpos);
+ void *X509_CRL_get_ext_d2i(const X509_CRL *x, int nid, int *crit, int *idx);
+
+ int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md);
+ int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r);
+
+ int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl);
+ X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl);
+ int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
+
+ int X509_CRL_print(BIO *bio, X509_CRL *crl);
+
+ int X509_CRL_get0_by_serial(X509_CRL *crl,
+ X509_REVOKED **ret, ASN1_INTEGER *serial);
+ int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x);
+
+ //STACK_OF(X509_REVOKED)
+ OPENSSL_STACK *X509_CRL_get_REVOKED(X509_CRL *crl);
+
+ int X509_CRL_get0_by_serial(X509_CRL *crl,
+ X509_REVOKED **ret, ASN1_INTEGER *serial);
+]]
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ int X509_CRL_set1_lastUpdate(X509_CRL *x, const ASN1_TIME *tm);
+ int X509_CRL_set1_nextUpdate(X509_CRL *x, const ASN1_TIME *tm);
+ /*const*/ ASN1_TIME *X509_CRL_get0_lastUpdate(const X509_CRL *crl);
+ /*const*/ ASN1_TIME *X509_CRL_get0_nextUpdate(const X509_CRL *crl);
+ long X509_CRL_get_version(const X509_CRL *crl);
+
+ X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc);
+
+ int X509_CRL_get_signature_nid(const X509_CRL *crl);
+ ]]
+end
+if OPENSSL_10 or BORINGSSL_110 then
+ -- in openssl 1.0.x some getters are direct accessor to struct members (defiend by macros)
+ ffi.cdef [[
+ typedef struct X509_crl_info_st {
+ ASN1_INTEGER *version;
+ X509_ALGOR *sig_alg;
+ X509_NAME *issuer;
+ ASN1_TIME *lastUpdate;
+ ASN1_TIME *nextUpdate;
+ // STACK_OF(X509_REVOKED)
+ OPENSSL_STACK *revoked;
+ // STACK_OF(X509_EXTENSION)
+ OPENSSL_STACK /* [0] */ *extensions;
+ ASN1_ENCODING enc;
+ } X509_CRL_INFO;
+
+ // Note: this struct is trimmed
+ struct X509_crl_st {
+ /* actual signature */
+ X509_CRL_INFO *crl;
+ // trimmed
+ } /* X509_CRL */ ;
+
+ int X509_CRL_set_lastUpdate(X509_CRL *x, const ASN1_TIME *tm);
+ int X509_CRL_set_nextUpdate(X509_CRL *x, const ASN1_TIME *tm);
+ ]]
+end
diff --git a/server/resty/openssl/include/x509/csr.lua b/server/resty/openssl/include/x509/csr.lua
new file mode 100644
index 0000000..44c4801
--- /dev/null
+++ b/server/resty/openssl/include/x509/csr.lua
@@ -0,0 +1,88 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.evp"
+require "resty.openssl.include.objects"
+require "resty.openssl.include.x509"
+require "resty.openssl.include.stack"
+
+local asn1_macro = require "resty.openssl.include.asn1"
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
+
+asn1_macro.declare_asn1_functions("X509_REQ", asn1_macro.has_new_ex)
+
+ffi.cdef [[
+ int X509_REQ_set_subject_name(X509_REQ *req, X509_NAME *name);
+
+ EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req);
+ int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
+
+ int X509_REQ_set_version(X509_REQ *x, long version);
+
+ int X509_REQ_get_attr_count(const X509_REQ *req);
+
+ int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
+ X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc);
+ int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid, int lastpos);
+
+ int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp);
+ void X509_ATTRIBUTE_free(X509_ATTRIBUTE *a);
+ int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, int lastpos);
+ X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc);
+
+ int *X509_REQ_get_extension_nids(void);
+
+ int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md);
+ int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r);
+
+ int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req);
+ X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req);
+
+ // STACK_OF(X509_EXTENSION)
+ OPENSSL_STACK *X509_REQ_get_extensions(X509_REQ *req);
+ // STACK_OF(X509_EXTENSION)
+ int X509_REQ_add_extensions(X509_REQ *req, OPENSSL_STACK *exts);
+
+ int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k);
+]]
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req);
+ long X509_REQ_get_version(const X509_REQ *req);
+
+ int X509_REQ_get_signature_nid(const X509_REQ *crl);
+ ]]
+end
+if OPENSSL_10 or BORINGSSL_110 then
+ ffi.cdef [[
+ typedef struct X509_req_info_st {
+ ASN1_ENCODING enc;
+ ASN1_INTEGER *version;
+ X509_NAME *subject;
+ /*X509_PUBKEY*/ void *pubkey;
+ /* d=2 hl=2 l= 0 cons: cont: 00 */
+ /*STACK_OF(X509_ATTRIBUTE)*/ OPENSSL_STACK *attributes; /* [ 0 ] */
+ } X509_REQ_INFO;
+
+ // Note: this struct is trimmed
+ typedef struct X509_req_st {
+ X509_REQ_INFO *req_info;
+ X509_ALGOR *sig_alg;
+ // trimmed
+ //ASN1_BIT_STRING *signature;
+ //int references;
+ } X509_REQ;
+ ]]
+end
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ int X509_REQ_verify_ex(X509_REQ *a, EVP_PKEY *pkey, OSSL_LIB_CTX *libctx,
+ const char *propq);
+ ]]
+end
diff --git a/server/resty/openssl/include/x509/extension.lua b/server/resty/openssl/include/x509/extension.lua
new file mode 100644
index 0000000..14b231e
--- /dev/null
+++ b/server/resty/openssl/include/x509/extension.lua
@@ -0,0 +1,44 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.x509"
+local asn1_macro = require "resty.openssl.include.asn1"
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+asn1_macro.declare_asn1_functions("X509_EXTENSION")
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ struct v3_ext_ctx {
+ int flags;
+ X509 *issuer_cert;
+ X509 *subject_cert;
+ X509_REQ *subject_req;
+ X509_CRL *crl;
+ /*X509V3_CONF_METHOD*/ void *db_meth;
+ void *db;
+ EVP_PKEY *issuer_pkey;
+ };
+
+ int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey);
+ ]]
+
+else
+ ffi.cdef [[
+ struct v3_ext_ctx {
+ int flags;
+ X509 *issuer_cert;
+ X509 *subject_cert;
+ X509_REQ *subject_req;
+ X509_CRL *crl;
+ /*X509V3_CONF_METHOD*/ void *db_meth;
+ void *db;
+ };
+ ]]
+end
+
+ffi.cdef [[
+ int X509_EXTENSION_set_data(X509_EXTENSION *ex, ASN1_OCTET_STRING *data);
+ int X509_EXTENSION_set_object(X509_EXTENSION *ex, const ASN1_OBJECT *obj);
+]] \ No newline at end of file
diff --git a/server/resty/openssl/include/x509/init.lua b/server/resty/openssl/include/x509/init.lua
new file mode 100644
index 0000000..ec104ef
--- /dev/null
+++ b/server/resty/openssl/include/x509/init.lua
@@ -0,0 +1,138 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.bio"
+require "resty.openssl.include.pem"
+require "resty.openssl.include.stack"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
+
+asn1_macro.declare_asn1_functions("X509", asn1_macro.has_new_ex)
+
+ffi.cdef [[
+ int i2d_X509_bio(BIO *bp, X509 *x509);
+ X509 *d2i_X509_bio(BIO *bp, X509 **x509);
+
+ // STACK_OF(X509)
+ OPENSSL_STACK *X509_chain_up_ref(OPENSSL_STACK *chain);
+
+ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
+ int X509_verify(X509 *a, EVP_PKEY *r);
+
+ ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj);
+
+ int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
+ X509_EXTENSION *X509_get_ext(const X509 *x, int loc);
+ int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos);
+ void *X509_get_ext_d2i(const X509 *x, int nid, int *crit, int *idx);
+
+ int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit);
+ int X509_EXTENSION_get_critical(const X509_EXTENSION *ex);
+ ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex);
+ ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
+ X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
+ X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
+ int nid, int crit,
+ ASN1_OCTET_STRING *data);
+
+ // needed by pkey
+ EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a);
+ EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a);
+ int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey);
+ int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey);
+
+ EVP_PKEY *X509_get_pubkey(X509 *x);
+ int X509_set_pubkey(X509 *x, EVP_PKEY *pkey);
+ int X509_set_version(X509 *x, long version);
+ int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial);
+
+ X509_NAME *X509_get_subject_name(const X509 *a);
+ int X509_set_subject_name(X509 *x, X509_NAME *name);
+ X509_NAME *X509_get_issuer_name(const X509 *a);
+ int X509_set_issuer_name(X509 *x, X509_NAME *name);
+
+ int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
+ unsigned char *md, unsigned int *len);
+ int X509_digest(const X509 *data, const EVP_MD *type,
+ unsigned char *md, unsigned int *len);
+
+ const char *X509_verify_cert_error_string(long n);
+ int X509_verify_cert(X509_STORE_CTX *ctx);
+
+ int X509_get_signature_nid(const X509 *x);
+
+ unsigned char *X509_alias_get0(X509 *x, int *len);
+ unsigned char *X509_keyid_get0(X509 *x, int *len);
+ int X509_check_private_key(X509 *x, EVP_PKEY *k);
+]]
+
+if OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ int X509_up_ref(X509 *a);
+
+ int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm);
+ int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm);
+ /*const*/ ASN1_TIME *X509_get0_notBefore(const X509 *x);
+ /*const*/ ASN1_TIME *X509_get0_notAfter(const X509 *x);
+ long X509_get_version(const X509 *x);
+ const ASN1_INTEGER *X509_get0_serialNumber(X509 *x);
+
+ X509_EXTENSION *X509_delete_ext(X509 *x, int loc);
+ ]]
+elseif OPENSSL_10 then
+ ffi.cdef [[
+ // STACK_OF(X509_EXTENSION)
+ X509_EXTENSION *X509v3_delete_ext(OPENSSL_STACK *x, int loc);
+ ]]
+end
+
+if OPENSSL_10 or BORINGSSL_110 then
+ -- in openssl 1.0.x some getters are direct accessor to struct members (defiend by macros)
+ ffi.cdef [[
+ // crypto/x509/x509.h
+ typedef struct X509_val_st {
+ ASN1_TIME *notBefore;
+ ASN1_TIME *notAfter;
+ } X509_VAL;
+
+ typedef struct X509_algor_st {
+ ASN1_OBJECT *algorithm;
+ ASN1_TYPE *parameter;
+ } X509_ALGOR;
+
+ // Note: this struct is trimmed
+ typedef struct x509_cinf_st {
+ /*ASN1_INTEGER*/ void *version;
+ /*ASN1_INTEGER*/ void *serialNumber;
+ X509_ALGOR *signature;
+ X509_NAME *issuer;
+ X509_VAL *validity;
+ X509_NAME *subject;
+ /*X509_PUBKEY*/ void *key;
+ /*ASN1_BIT_STRING*/ void *issuerUID; /* [ 1 ] optional in v2 */
+ /*ASN1_BIT_STRING*/ void *subjectUID; /* [ 2 ] optional in v2 */
+ /*STACK_OF(X509_EXTENSION)*/ OPENSSL_STACK *extensions; /* [ 3 ] optional in v3 */
+ // trimmed
+ // ASN1_ENCODING enc;
+ } X509_CINF;
+ // Note: this struct is trimmed
+ struct x509_st {
+ X509_CINF *cert_info;
+ // trimmed
+ } X509;
+
+ int X509_set_notBefore(X509 *x, const ASN1_TIME *tm);
+ int X509_set_notAfter(X509 *x, const ASN1_TIME *tm);
+ ASN1_INTEGER *X509_get_serialNumber(X509 *x);
+ ]]
+end
+
+if BORINGSSL_110 then
+ ffi.cdef [[
+ ASN1_TIME *X509_get_notBefore(const X509 *x);
+ ASN1_TIME *X509_get_notAfter(const X509 *x);
+ ]]
+end
diff --git a/server/resty/openssl/include/x509/name.lua b/server/resty/openssl/include/x509/name.lua
new file mode 100644
index 0000000..2f933ae
--- /dev/null
+++ b/server/resty/openssl/include/x509/name.lua
@@ -0,0 +1,21 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.asn1"
+require "resty.openssl.include.objects"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+asn1_macro.declare_asn1_functions("X509_NAME")
+
+ffi.cdef [[
+ int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type,
+ const unsigned char *bytes, int len, int loc,
+ int set);
+
+ int X509_NAME_entry_count(const X509_NAME *name);
+ X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc);
+ ASN1_OBJECT *X509_NAME_ENTRY_get_object(const X509_NAME_ENTRY *ne);
+ ASN1_STRING * X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne);
+ int X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
+ int lastpos);
+]] \ No newline at end of file
diff --git a/server/resty/openssl/include/x509/revoked.lua b/server/resty/openssl/include/x509/revoked.lua
new file mode 100644
index 0000000..c6539c9
--- /dev/null
+++ b/server/resty/openssl/include/x509/revoked.lua
@@ -0,0 +1,17 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.asn1"
+require "resty.openssl.include.objects"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+asn1_macro.declare_asn1_functions("X509_REVOKED")
+
+ffi.cdef [[
+ int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial);
+ int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm);
+ int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc);
+
+ const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *r);
+ const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *r);
+]] \ No newline at end of file
diff --git a/server/resty/openssl/include/x509_vfy.lua b/server/resty/openssl/include/x509_vfy.lua
new file mode 100644
index 0000000..d783d19
--- /dev/null
+++ b/server/resty/openssl/include/x509_vfy.lua
@@ -0,0 +1,108 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.stack"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
+
+ffi.cdef [[
+ X509_STORE *X509_STORE_new(void);
+ void X509_STORE_free(X509_STORE *v);
+ /* int X509_STORE_lock(X509_STORE *ctx);
+ int X509_STORE_unlock(X509_STORE *ctx);
+ int X509_STORE_up_ref(X509_STORE *v);
+ // STACK_OF(X509_OBJECT)
+ OPENSSL_STACK *X509_STORE_get0_objects(X509_STORE *v);*/
+
+ int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
+ int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
+ int X509_STORE_load_locations(X509_STORE *ctx,
+ const char *file, const char *dir);
+ int X509_STORE_set_default_paths(X509_STORE *ctx);
+ int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
+ int X509_STORE_set_depth(X509_STORE *store, int depth);
+ int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
+
+ X509_STORE_CTX *X509_STORE_CTX_new(void);
+ void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
+ // STACK_OF(X509)
+ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
+ X509 *x509, OPENSSL_STACK *chain);
+
+ int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
+
+ int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name);
+
+ int X509_PURPOSE_get_by_sname(char *sname);
+ X509_PURPOSE *X509_PURPOSE_get0(int idx);
+ int X509_PURPOSE_get_id(const X509_PURPOSE *xp);
+]]
+
+local _M = {
+ verify_flags = {
+ X509_V_FLAG_CB_ISSUER_CHECK = 0x0, -- Deprecated
+ X509_V_FLAG_USE_CHECK_TIME = 0x2,
+ X509_V_FLAG_CRL_CHECK = 0x4,
+ X509_V_FLAG_CRL_CHECK_ALL = 0x8,
+ X509_V_FLAG_IGNORE_CRITICAL = 0x10,
+ X509_V_FLAG_X509_STRICT = 0x20,
+ X509_V_FLAG_ALLOW_PROXY_CERTS = 0x40,
+ X509_V_FLAG_POLICY_CHECK = 0x80,
+ X509_V_FLAG_EXPLICIT_POLICY = 0x100,
+ X509_V_FLAG_INHIBIT_ANY = 0x200,
+ X509_V_FLAG_INHIBIT_MAP = 0x400,
+ X509_V_FLAG_NOTIFY_POLICY = 0x800,
+ X509_V_FLAG_EXTENDED_CRL_SUPPORT = 0x1000,
+ X509_V_FLAG_USE_DELTAS = 0x2000,
+ X509_V_FLAG_CHECK_SS_SIGNATURE = 0x4000,
+ X509_V_FLAG_TRUSTED_FIRST = 0x8000,
+ X509_V_FLAG_SUITEB_128_LOS_ONLY = 0x10000,
+ X509_V_FLAG_SUITEB_192_LOS = 0x20000,
+ X509_V_FLAG_SUITEB_128_LOS = 0x30000,
+ X509_V_FLAG_PARTIAL_CHAIN = 0x80000,
+ X509_V_FLAG_NO_ALT_CHAINS = 0x100000,
+ X509_V_FLAG_NO_CHECK_TIME = 0x200000,
+ },
+}
+
+if OPENSSL_10 or BORINGSSL_110 then
+ ffi.cdef [[
+ // STACK_OF(X509)
+ OPENSSL_STACK *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
+ ]];
+ _M.X509_STORE_CTX_get0_chain = C.X509_STORE_CTX_get_chain
+elseif OPENSSL_11_OR_LATER then
+ ffi.cdef [[
+ // STACK_OF(X509)
+ OPENSSL_STACK *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
+ ]];
+ _M.X509_STORE_CTX_get0_chain = C.X509_STORE_CTX_get0_chain
+end
+
+if OPENSSL_3X then
+ ffi.cdef [[
+ X509_STORE_CTX *X509_STORE_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
+
+ int X509_STORE_set_default_paths_ex(X509_STORE *ctx, OSSL_LIB_CTX *libctx,
+ const char *propq);
+ /* int X509_STORE_load_file_ex(X509_STORE *ctx, const char *file,
+ OSSL_LIB_CTX *libctx, const char *propq);
+ int X509_STORE_load_store_ex(X509_STORE *ctx, const char *uri,
+ OSSL_LIB_CTX *libctx, const char *propq); */
+ int X509_STORE_load_locations_ex(X509_STORE *ctx, const char *file,
+ const char *dir, OSSL_LIB_CTX *libctx,
+ const char *propq);
+ ]]
+ _M.X509_STORE_set_default_paths = function(...) return C.X509_STORE_set_default_paths_ex(...) end
+ _M.X509_STORE_load_locations = function(...) return C.X509_STORE_load_locations_ex(...) end
+else
+ _M.X509_STORE_set_default_paths = function(s) return C.X509_STORE_set_default_paths(s) end
+ _M.X509_STORE_load_locations = function(s, file, dir) return C.X509_STORE_load_locations(s, file, dir) end
+end
+
+
+return _M
+
diff --git a/server/resty/openssl/include/x509v3.lua b/server/resty/openssl/include/x509v3.lua
new file mode 100644
index 0000000..6882c6e
--- /dev/null
+++ b/server/resty/openssl/include/x509v3.lua
@@ -0,0 +1,108 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.ossl_typ"
+require "resty.openssl.include.stack"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+ffi.cdef [[
+ // STACK_OF(OPENSSL_STRING)
+ OPENSSL_STACK *X509_get1_ocsp(X509 *x);
+ void X509_email_free(OPENSSL_STACK *sk);
+ void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
+
+ typedef struct EDIPartyName_st EDIPARTYNAME;
+
+ typedef struct otherName_st OTHERNAME;
+
+ typedef struct GENERAL_NAME_st {
+ int type;
+ union {
+ char *ptr;
+ OTHERNAME *otherName; /* otherName */
+ ASN1_IA5STRING *rfc822Name;
+ ASN1_IA5STRING *dNSName;
+ ASN1_TYPE *x400Address;
+ X509_NAME *directoryName;
+ EDIPARTYNAME *ediPartyName;
+ ASN1_IA5STRING *uniformResourceIdentifier;
+ ASN1_OCTET_STRING *iPAddress;
+ ASN1_OBJECT *registeredID;
+ /* Old names */
+ ASN1_OCTET_STRING *ip; /* iPAddress */
+ X509_NAME *dirn; /* dirn */
+ ASN1_IA5STRING *ia5; /* rfc822Name, dNSName,
+ * uniformResourceIdentifier */
+ ASN1_OBJECT *rid; /* registeredID */
+ ASN1_TYPE *other; /* x400Address */
+ } d;
+ } GENERAL_NAME;
+
+ // STACK_OF(GENERAL_NAME)
+ typedef struct stack_st GENERAL_NAMES;
+
+ // STACK_OF(X509_EXTENSION)
+ int X509V3_add1_i2d(OPENSSL_STACK **x, int nid, void *value,
+ int crit, unsigned long flags);
+ void *X509V3_EXT_d2i(X509_EXTENSION *ext);
+ X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
+ int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag,
+ int indent);
+
+ int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
+ unsigned long flags);
+ // although the struct has plural form, it's not a stack
+ typedef struct BASIC_CONSTRAINTS_st {
+ int ca;
+ ASN1_INTEGER *pathlen;
+ } BASIC_CONSTRAINTS;
+
+ void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
+ X509_REQ *req, X509_CRL *crl, int flags);
+
+ X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid,
+ const char *value);
+ X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, const char *name,
+ const char *value);
+ int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag,
+ int indent);
+
+ void *X509V3_get_d2i(const OPENSSL_STACK *x, int nid, int *crit, int *idx);
+
+ int X509v3_get_ext_by_NID(const OPENSSL_STACK *x,
+ int nid, int lastpos);
+
+ X509_EXTENSION *X509v3_get_ext(const OPENSSL_STACK *x, int loc);
+
+ // STACK_OF(ACCESS_DESCRIPTION)
+ typedef struct stack_st AUTHORITY_INFO_ACCESS;
+
+ typedef struct ACCESS_DESCRIPTION_st {
+ ASN1_OBJECT *method;
+ GENERAL_NAME *location;
+ } ACCESS_DESCRIPTION;
+
+ typedef struct DIST_POINT_NAME_st {
+ int type;
+ union {
+ GENERAL_NAMES *fullname;
+ // STACK_OF(X509_NAME_ENTRY)
+ OPENSSL_STACK *relativename;
+ } name;
+ /* If relativename then this contains the full distribution point name */
+ X509_NAME *dpname;
+ } DIST_POINT_NAME;
+
+ typedef struct DIST_POINT_st {
+ DIST_POINT_NAME *distpoint;
+ ASN1_BIT_STRING *reasons;
+ GENERAL_NAMES *CRLissuer;
+ int dp_reasons;
+ } DIST_POINT;
+
+]]
+
+asn1_macro.declare_asn1_functions("GENERAL_NAME")
+asn1_macro.declare_asn1_functions("BASIC_CONSTRAINTS")
+asn1_macro.declare_asn1_functions("AUTHORITY_INFO_ACCESS") -- OCSP responder and CA
+asn1_macro.declare_asn1_functions("ACCESS_DESCRIPTION")
+asn1_macro.declare_asn1_functions("DIST_POINT") -- CRL distribution points
diff --git a/server/resty/openssl/kdf.lua b/server/resty/openssl/kdf.lua
new file mode 100644
index 0000000..62188bc
--- /dev/null
+++ b/server/resty/openssl/kdf.lua
@@ -0,0 +1,388 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require("resty.openssl.objects")
+require("resty.openssl.include.evp.md")
+-- used by legacy EVP_PKEY_derive interface
+require("resty.openssl.include.evp.pkey")
+local kdf_macro = require "resty.openssl.include.evp.kdf"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_error
+local version_num = require("resty.openssl.version").version_num
+local version_text = require("resty.openssl.version").version_text
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+
+--[[
+https://wiki.openssl.org/index.php/EVP_Key_Derivation
+
+OpenSSL 1.0.2 and above provides PBKDF2 by way of PKCS5_PBKDF2_HMAC and PKCS5_PBKDF2_HMAC_SHA1.
+OpenSSL 1.1.0 and above additionally provides HKDF and TLS1 PRF KDF by way of EVP_PKEY_derive and Scrypt by way of EVP_PBE_scrypt
+OpenSSL 1.1.1 and above additionally provides Scrypt by way of EVP_PKEY_derive.
+OpenSSL 3.0 additionally provides Single Step KDF, SSH KDF, PBKDF2, Scrypt, HKDF, ANSI X9.42 KDF, ANSI X9.63 KDF and TLS1 PRF KDF by way of EVP_KDF.
+From OpenSSL 3.0 the recommended way of performing key derivation is to use the EVP_KDF functions. If compatibility with OpenSSL 1.1.1 is required then a limited set of KDFs can be used via EVP_PKEY_derive.
+]]
+
+local NID_id_pbkdf2 = -1
+local NID_id_scrypt = -2
+local NID_tls1_prf = -3
+local NID_hkdf = -4
+if version_num >= 0x10002000 then
+ NID_id_pbkdf2 = C.OBJ_txt2nid("PBKDF2")
+ assert(NID_id_pbkdf2 > 0)
+end
+if version_num >= 0x10100000 and not BORINGSSL then
+ NID_hkdf = C.OBJ_txt2nid("HKDF")
+ assert(NID_hkdf > 0)
+ NID_tls1_prf = C.OBJ_txt2nid("TLS1-PRF")
+ assert(NID_tls1_prf > 0)
+ -- we use EVP_PBE_scrypt to do scrypt, so this is supported >= 1.1.0
+ NID_id_scrypt = C.OBJ_txt2nid("id-scrypt")
+ assert(NID_id_scrypt > 0)
+end
+
+local _M = {
+ HKDEF_MODE_EXTRACT_AND_EXPAND = kdf_macro.EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND,
+ HKDEF_MODE_EXTRACT_ONLY = kdf_macro.EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY,
+ HKDEF_MODE_EXPAND_ONLY = kdf_macro.EVP_PKEY_HKDEF_MODE_EXPAND_ONLY,
+
+ PBKDF2 = NID_id_pbkdf2,
+ SCRYPT = NID_id_scrypt,
+ TLS1_PRF = NID_tls1_prf,
+ HKDF = NID_hkdf,
+}
+
+local type_literals = {
+ [NID_id_pbkdf2] = "PBKDF2",
+ [NID_id_scrypt] = "scrypt",
+ [NID_tls1_prf] = "TLS-1PRF",
+ [NID_hkdf] = "HKDF",
+}
+
+local TYPE_NUMBER = 0x1
+local TYPE_STRING = 0x2
+
+local function check_options(opt, nid, field, typ, is_optional, required_only_if_nid)
+ local v = opt[field]
+ if not v then
+ if is_optional or (required_only_if_nid and required_only_if_nid ~= nid) then
+ return typ == TYPE_NUMBER and 0 or nil
+ else
+ return nil, "\"" .. field .. "\" must be set"
+ end
+ end
+
+ if typ == TYPE_NUMBER then
+ v = tonumber(v)
+ if not typ then
+ return nil, "except a number as \"" .. field .. "\""
+ end
+ elseif typ == TYPE_STRING then
+ if type(v) ~= "string" then
+ return nil, "except a string as \"" .. field .. "\""
+ end
+ else
+ error("don't known how to check " .. typ, 2)
+ end
+
+ return v
+end
+
+local function check_hkdf_options(opt)
+ local mode = opt.hkdf_mode
+ if not mode or version_num < 0x10101000 then
+ mode = _M.HKDEF_MODE_EXTRACT_AND_EXPAND
+ end
+
+ if mode == _M.HKDEF_MODE_EXTRACT_AND_EXPAND and (
+ not opt.salt or not opt.hkdf_info) then
+ return '""salt" and "hkdf_info" are required for EXTRACT_AND_EXPAND mode'
+ elseif mode == _M.HKDEF_MODE_EXTRACT_ONLY and not opt.salt then
+ return '"salt" is required for EXTRACT_ONLY mode'
+ elseif mode == _M.EVP_PKEY_HKDEF_MODE_EXPAND_ONLY and not opt.hkdf_info then
+ return '"hkdf_info" is required for EXPAND_ONLY mode'
+ end
+
+ return nil
+end
+
+local options_schema = {
+ outlen = { TYPE_NUMBER },
+ pass = { TYPE_STRING, true },
+ salt = { TYPE_STRING, true },
+ md = { TYPE_STRING, true },
+ -- pbkdf2 only
+ pbkdf2_iter = { TYPE_NUMBER, true },
+ -- hkdf only
+ hkdf_key = { TYPE_STRING, nil, NID_hkdf },
+ hkdf_mode = { TYPE_NUMBER, true },
+ hkdf_info = { TYPE_STRING, true },
+ -- tls1-prf
+ tls1_prf_secret = { TYPE_STRING, nil, NID_tls1_prf },
+ tls1_prf_seed = { TYPE_STRING, nil, NID_tls1_prf },
+ -- scrypt only
+ scrypt_maxmem = { TYPE_NUMBER, true },
+ scrypt_N = { TYPE_NUMBER, nil, NID_id_scrypt },
+ scrypt_r = { TYPE_NUMBER, nil, NID_id_scrypt },
+ scrypt_p = { TYPE_NUMBER, nil, NID_id_scrypt },
+}
+
+local outlen = ctypes.ptr_of_uint64()
+
+function _M.derive(options)
+ local typ = options.type
+ if not typ then
+ return nil, "kdf.derive: \"type\" must be set"
+ elseif type(typ) ~= "number" then
+ return nil, "kdf.derive: expect a number as \"type\""
+ end
+
+ if typ <= 0 then
+ return nil, "kdf.derive: kdf type " .. (type_literals[typ] or tostring(typ)) ..
+ " not supported in " .. version_text
+ end
+
+ for k, v in pairs(options_schema) do
+ local v, err = check_options(options, typ, k, unpack(v))
+ if err then
+ return nil, "kdf.derive: " .. err
+ end
+ options[k] = v
+ end
+
+ if typ == NID_hkdf then
+ local err = check_hkdf_options(options)
+ if err then
+ return nil, "kdf.derive: " .. err
+ end
+ end
+
+ local salt_len = 0
+ if options.salt then
+ salt_len = #options.salt
+ end
+ local pass_len = 0
+ if options.pass then
+ pass_len = #options.pass
+ end
+
+ local md
+ if OPENSSL_3X then
+ md = C.EVP_MD_fetch(ctx_lib.get_libctx(), options.md or 'sha1', options.properties)
+ else
+ md = C.EVP_get_digestbyname(options.md or 'sha1')
+ end
+ if md == nil then
+ return nil, string.format("kdf.derive: invalid digest type \"%s\"", md)
+ end
+
+ local buf = ctypes.uchar_array(options.outlen)
+
+ -- begin legacay low level routines
+ local code
+ if typ == NID_id_pbkdf2 then
+ -- make openssl 1.0.2 happy
+ if version_num < 0x10100000 and not options.pass then
+ options.pass = ""
+ pass_len = 0
+ end
+ -- https://www.openssl.org/docs/man1.1.0/man3/PKCS5_PBKDF2_HMAC.html
+ local iter = options.pbkdf2_iter
+ if iter < 1 then
+ iter = 1
+ end
+ code = C.PKCS5_PBKDF2_HMAC(
+ options.pass, pass_len,
+ options.salt, salt_len, iter,
+ md, options.outlen, buf
+ )
+ elseif typ == NID_id_scrypt then
+ code = C.EVP_PBE_scrypt(
+ options.pass, pass_len,
+ options.salt, salt_len,
+ options.scrypt_N, options.scrypt_r, options.scrypt_p, options.scrypt_maxmem,
+ buf, options.outlen
+ )
+ elseif typ ~= NID_tls1_prf and typ ~= NID_hkdf then
+ return nil, string.format("kdf.derive: unknown type %d", typ)
+ end
+ if code then
+ if code ~= 1 then
+ return nil, format_error("kdf.derive")
+ else
+ return ffi_str(buf, options.outlen)
+ end
+ end
+ -- end legacay low level routines
+
+ -- begin EVP_PKEY_derive routines
+ outlen[0] = options.outlen
+
+ local ctx = C.EVP_PKEY_CTX_new_id(typ, nil)
+ if ctx == nil then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_new_id")
+ end
+ ffi_gc(ctx, C.EVP_PKEY_CTX_free)
+ if C.EVP_PKEY_derive_init(ctx) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_derive_init")
+ end
+
+ if typ == NID_tls1_prf then
+ if kdf_macro.EVP_PKEY_CTX_set_tls1_prf_md(ctx, md) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_tls1_prf_md")
+ end
+ if kdf_macro.EVP_PKEY_CTX_set1_tls1_prf_secret(ctx, options.tls1_prf_secret) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_tls1_prf_secret")
+ end
+ if kdf_macro.EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, options.tls1_prf_seed) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_add1_tls1_prf_seed")
+ end
+ elseif typ == NID_hkdf then
+ if kdf_macro.EVP_PKEY_CTX_set_hkdf_md(ctx, md) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_hkdf_md")
+ end
+ if options.salt and
+ kdf_macro.EVP_PKEY_CTX_set1_hkdf_salt(ctx, options.salt) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_hkdf_salt")
+ end
+ if options.hkdf_key and
+ kdf_macro.EVP_PKEY_CTX_set1_hkdf_key(ctx, options.hkdf_key) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_hkdf_key")
+ end
+ if options.hkdf_info and
+ kdf_macro.EVP_PKEY_CTX_add1_hkdf_info(ctx, options.hkdf_info) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_add1_hkdf_info")
+ end
+ if options.hkdf_mode then
+ if version_num >= 0x10101000 then
+ if kdf_macro.EVP_PKEY_CTX_set_hkdf_mode(ctx, options.hkdf_mode) ~= 1 then
+ return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_hkdf_mode")
+ end
+ if options.hkdf_mode == _M.HKDEF_MODE_EXTRACT_ONLY then
+ local md_size = OPENSSL_3X and C.EVP_MD_get_size(md) or C.EVP_MD_size(md)
+ if options.outlen ~= md_size then
+ options.outlen = md_size
+ ngx.log(ngx.WARN, "hkdf_mode EXTRACT_ONLY outputs fixed length of ", md_size,
+ " key, ignoring options.outlen")
+ end
+ outlen[0] = md_size
+ buf = ctypes.uchar_array(md_size)
+ end
+ else
+ ngx.log(ngx.WARN, "hkdf_mode is not effective in ", version_text)
+ end
+ end
+ else
+ return nil, string.format("kdf.derive: unknown type %d", typ)
+ end
+ code = C.EVP_PKEY_derive(ctx, buf, outlen)
+ if code == -2 then
+ return nil, "kdf.derive: operation is not supported by the public key algorithm"
+ end
+ -- end EVP_PKEY_derive routines
+
+ return ffi_str(buf, options.outlen)
+end
+
+if not OPENSSL_3X then
+ return _M
+end
+
+_M.derive_legacy = _M.derive
+_M.derive = nil
+
+-- OPENSSL 3.0 style API
+local param_lib = require "resty.openssl.param"
+local SIZE_MAX = ctypes.SIZE_MAX
+
+local mt = {__index = _M}
+
+local kdf_ctx_ptr_ct = ffi.typeof('EVP_KDF_CTX*')
+
+function _M.new(typ, properties)
+ local algo = C.EVP_KDF_fetch(ctx_lib.get_libctx(), typ, properties)
+ if algo == nil then
+ return nil, format_error(string.format("mac.new: invalid mac type \"%s\"", typ))
+ end
+
+ local ctx = C.EVP_KDF_CTX_new(algo)
+ if ctx == nil then
+ return nil, "mac.new: failed to create EVP_MAC_CTX"
+ end
+ ffi_gc(ctx, C.EVP_KDF_CTX_free)
+
+ local buf
+ local buf_size = tonumber(C.EVP_KDF_CTX_get_kdf_size(ctx))
+ if buf_size == SIZE_MAX then -- no fixed size
+ buf_size = nil
+ else
+ buf = ctypes.uchar_array(buf_size)
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ algo = algo,
+ buf = buf,
+ buf_size = buf_size,
+ }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(kdf_ctx_ptr_ct, l.ctx)
+end
+
+function _M:get_provider_name()
+ local p = C.EVP_KDF_get0_provider(self.algo)
+ if p == nil then
+ return nil
+ end
+ return ffi_str(C.OSSL_PROVIDER_get0_name(p))
+end
+
+_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_KDF_CTX")
+
+function _M:derive(outlen, options, options_count)
+ if not _M.istype(self) then
+ return _M.derive_legacy(self)
+ end
+
+ if self.buf_size and outlen then
+ return nil, string.format("kdf:derive: this KDF has fixed output size %d, "..
+ "it can't be set manually", self.buf_size)
+ end
+
+ outlen = self.buf_size or outlen
+ local buf = self.buf or ctypes.uchar_array(outlen)
+
+ if options_count then
+ options_count = options_count - 1
+ else
+ options_count = 0
+ for k, v in pairs(options) do options_count = options_count + 1 end
+ end
+
+ local param, err
+ if options_count > 0 then
+ local schema = self:settable_params(true) -- raw schema
+ param, err = param_lib.construct(options, nil, schema)
+ if err then
+ return nil, "kdf:derive: " .. err
+ end
+ end
+
+ if C.EVP_KDF_derive(self.ctx, buf, outlen, param) ~= 1 then
+ return nil, format_error("kdf:derive")
+ end
+
+ return ffi_str(buf, outlen)
+end
+
+function _M:reset()
+ C.EVP_KDF_CTX_reset(self.ctx)
+ return true
+end
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/mac.lua b/server/resty/openssl/mac.lua
new file mode 100644
index 0000000..65f5e38
--- /dev/null
+++ b/server/resty/openssl/mac.lua
@@ -0,0 +1,96 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.evp.mac"
+local param_lib = require "resty.openssl.param"
+local ctx_lib = require "resty.openssl.ctx"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = {__index = _M}
+
+local mac_ctx_ptr_ct = ffi.typeof('EVP_MAC_CTX*')
+local param_types = {
+ cipher = param_lib.OSSL_PARAM_UTF8_STRING,
+ digest = param_lib.OSSL_PARAM_UTF8_STRING,
+}
+local params = {}
+
+function _M.new(key, typ, cipher, digest, properties)
+ if not OPENSSL_3X then
+ return false, "EVP_MAC is only supported from OpenSSL 3.0"
+ end
+
+ local algo = C.EVP_MAC_fetch(ctx_lib.get_libctx(), typ, properties)
+ if algo == nil then
+ return nil, format_error(string.format("mac.new: invalid mac type \"%s\"", typ))
+ end
+
+ local ctx = C.EVP_MAC_CTX_new(algo)
+ if ctx == nil then
+ return nil, "mac.new: failed to create EVP_MAC_CTX"
+ end
+ ffi_gc(ctx, C.EVP_MAC_CTX_free)
+
+ params.digest = digest
+ params.cipher = cipher
+ local p = param_lib.construct(params, 2, param_types)
+
+ local code = C.EVP_MAC_init(ctx, key, #key, p)
+ if code ~= 1 then
+ return nil, format_error(string.format("mac.new: invalid cipher or digest type"))
+ end
+
+ local md_size = C.EVP_MAC_CTX_get_mac_size(ctx)
+
+ return setmetatable({
+ ctx = ctx,
+ algo = algo,
+ buf = ctypes.uchar_array(md_size),
+ buf_size = md_size,
+ }, mt), nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(mac_ctx_ptr_ct, l.ctx)
+end
+
+function _M:get_provider_name()
+ local p = C.EVP_MAC_get0_provider(self.algo)
+ if p == nil then
+ return nil
+ end
+ return ffi_str(C.OSSL_PROVIDER_get0_name(p))
+end
+
+_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_MAC_CTX")
+
+function _M:update(...)
+ for _, s in ipairs({...}) do
+ if C.EVP_MAC_update(self.ctx, s, #s) ~= 1 then
+ return false, format_error("digest:update")
+ end
+ end
+ return true, nil
+end
+
+function _M:final(s)
+ if s then
+ local _, err = self:update(s)
+ if err then
+ return nil, err
+ end
+ end
+
+ local length = ctypes.ptr_of_size_t()
+ if C.EVP_MAC_final(self.ctx, self.buf, length, self.buf_size) ~= 1 then
+ return nil, format_error("digest:final: EVP_MAC_final")
+ end
+ return ffi_str(self.buf, length[0])
+end
+
+return _M
diff --git a/server/resty/openssl/objects.lua b/server/resty/openssl/objects.lua
new file mode 100644
index 0000000..bd02a38
--- /dev/null
+++ b/server/resty/openssl/objects.lua
@@ -0,0 +1,74 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+local ffi_sizeof = ffi.sizeof
+
+require "resty.openssl.include.objects"
+require "resty.openssl.include.err"
+
+local buf = ffi.new('char[?]', 100)
+
+local function obj2table(obj)
+ local nid = C.OBJ_obj2nid(obj)
+
+ local len = C.OBJ_obj2txt(buf, ffi_sizeof(buf), obj, 1)
+ local oid = ffi_str(buf, len)
+
+ return {
+ id = oid,
+ nid = nid,
+ sn = ffi_str(C.OBJ_nid2sn(nid)),
+ ln = ffi_str(C.OBJ_nid2ln(nid)),
+ }
+end
+
+local function nid2table(nid)
+ return obj2table(C.OBJ_nid2obj(nid))
+end
+
+local function txt2nid(txt)
+ if type(txt) ~= "string" then
+ return nil, "objects.txt2nid: expect a string at #1"
+ end
+ local nid = C.OBJ_txt2nid(txt)
+ if nid == 0 then
+ -- clean up error occurs during OBJ_txt2nid
+ C.ERR_clear_error()
+ return nil, "objects.txt2nid: invalid NID text " .. txt
+ end
+ return nid
+end
+
+local function txtnid2nid(txt_nid)
+ local nid
+ if type(txt_nid) == "string" then
+ nid = C.OBJ_txt2nid(txt_nid)
+ if nid == 0 then
+ -- clean up error occurs during OBJ_txt2nid
+ C.ERR_clear_error()
+ return nil, "objects.txtnid2nid: invalid NID text " .. txt_nid
+ end
+ elseif type(txt_nid) == "number" then
+ nid = txt_nid
+ else
+ return nil, "objects.txtnid2nid: expect string or number at #1"
+ end
+ return nid
+end
+
+local function find_sigid_algs(nid)
+ local out = ffi.new("int[0]")
+ if C.OBJ_find_sigid_algs(nid, out, nil) == 0 then
+ return 0, "objects.find_sigid_algs: invalid sigid " .. nid
+ end
+ return tonumber(out[0])
+end
+
+return {
+ obj2table = obj2table,
+ nid2table = nid2table,
+ txt2nid = txt2nid,
+ txtnid2nid = txtnid2nid,
+ find_sigid_algs = find_sigid_algs,
+ create = C.OBJ_create,
+} \ No newline at end of file
diff --git a/server/resty/openssl/param.lua b/server/resty/openssl/param.lua
new file mode 100644
index 0000000..2c8dcea
--- /dev/null
+++ b/server/resty/openssl/param.lua
@@ -0,0 +1,322 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_new = ffi.new
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.param"
+local format_error = require("resty.openssl.err").format_error
+local bn_lib = require("resty.openssl.bn")
+local null = require("resty.openssl.auxiliary.ctypes").null
+
+local OSSL_PARAM_INTEGER = 1
+local OSSL_PARAM_UNSIGNED_INTEGER = 2
+local OSSL_PARAM_REAL = 3
+local OSSL_PARAM_UTF8_STRING = 4
+local OSSL_PARAM_OCTET_STRING = 5
+local OSSL_PARAM_UTF8_PTR = 6
+local OSSL_PARAM_OCTET_PTR = 7
+
+local alter_type_key = {}
+local buf_param_key = {}
+
+local function construct(buf_t, length, types_map, types_size)
+ if not length then
+ length = 0
+ for k, v in pairs(buf_t) do length = length + 1 end
+ end
+
+ local params = ffi_new("OSSL_PARAM[?]", length + 1)
+
+ local i = 0
+ local buf_param
+ for key, value in pairs(buf_t) do
+ local typ = types_map[key]
+ if not typ then
+ return nil, "param:construct: unknown key \"" .. key .. "\""
+ end
+ local param, buf, size
+ if value == null then -- out
+ value = nil
+ size = types_size and types_size[key] or 100
+ if typ == OSSL_PARAM_UTF8_STRING or typ == OSSL_PARAM_OCTET_STRING then
+ buf = ffi_new("char[?]", size)
+ end
+ else
+ local numeric = type(value) == "number"
+ if (numeric and typ >= OSSL_PARAM_UTF8_STRING) or
+ (not numeric and typ <= OSSL_PARAM_UNSIGNED_INTEGER) then
+ local alter_typ = types_map[alter_type_key] and types_map[alter_type_key][key]
+ if alter_typ and ((numeric and alter_typ <= OSSL_PARAM_UNSIGNED_INTEGER) or
+ (not numeric and alter_typ >= OSSL_PARAM_UTF8_STRING)) then
+ typ = alter_typ
+ else
+ return nil, "param:construct: key \"" .. key .. "\" can't be a " .. type(value)
+ end
+ end
+ end
+
+ if typ == "bn" then -- out only
+ buf = ffi_new("char[?]", size)
+ param = C.OSSL_PARAM_construct_BN(key, buf, size)
+ buf_param = buf_param or {}
+ buf_param[key] = param
+ elseif typ == OSSL_PARAM_INTEGER then
+ buf = value and ffi_new("int[1]", value) or ffi_new("int[1]")
+ param = C.OSSL_PARAM_construct_int(key, buf)
+ elseif typ == OSSL_PARAM_UNSIGNED_INTEGER then
+ buf = value and ffi_new("unsigned int[1]", value) or
+ ffi_new("unsigned int[1]")
+ param = C.OSSL_PARAM_construct_uint(key, buf)
+ elseif typ == OSSL_PARAM_UTF8_STRING then
+ buf = value and ffi_cast("char *", value) or buf
+ param = C.OSSL_PARAM_construct_utf8_string(key, buf, value and #value or size)
+ elseif typ == OSSL_PARAM_OCTET_STRING then
+ buf = value and ffi_cast("char *", value) or buf
+ param = C.OSSL_PARAM_construct_octet_string(key, ffi_cast("void*", buf),
+ value and #value or size)
+ elseif typ == OSSL_PARAM_UTF8_PTR then
+ buf = ffi_new("char*[1]")
+ param = C.OSSL_PARAM_construct_utf8_ptr(key, buf, 0)
+ elseif typ == OSSL_PARAM_OCTET_PTR then
+ buf = ffi_new("char*[1]")
+ param = C.OSSL_PARAM_construct_octet_ptr(key, ffi_cast("void**", buf), 0)
+ else
+ error("type " .. typ .. " is not yet implemented")
+ end
+ if not value then -- out
+ buf_t[key] = buf
+ end
+ params[i] = param
+ i = i + 1
+ end
+
+ buf_t[buf_param_key] = buf_param
+ params[length] = C.OSSL_PARAM_construct_end()
+
+ return params
+end
+
+local function parse(buf_t, length, types_map, types_size)
+ for key, buf in pairs(buf_t) do
+ local typ = types_map[key]
+ local sz = types_size and types_size[key]
+
+ if key == buf_param_key then -- luacheck: ignore
+ -- ignore
+ elseif buf == nil or buf[0] == nil then
+ buf_t[key] = nil
+ elseif typ == "bn" then
+ local bn_t = ffi_new("BIGNUM*[1]")
+ local param = buf_t[buf_param_key][key]
+ if C.OSSL_PARAM_get_BN(param, bn_t) ~= 1 then
+ return nil, format_error("param:parse: OSSL_PARAM_get_BN")
+ end
+ buf_t[key] = bn_lib.dup(bn_t[0])
+ elseif typ == OSSL_PARAM_INTEGER or
+ typ == OSSL_PARAM_UNSIGNED_INTEGER then
+ buf_t[key] = tonumber(buf[0])
+ elseif typ == OSSL_PARAM_UTF8_STRING or
+ typ == OSSL_PARAM_OCTET_STRING then
+ buf_t[key] = sz and ffi_str(buf, sz) or ffi_str(buf)
+ elseif typ == OSSL_PARAM_UTF8_PTR or
+ typ == OSSL_PARAM_OCTET_PTR then
+ buf_t[key] = sz and ffi_str(buf[0], sz) or ffi_str(buf[0])
+ elseif not typ then
+ return nil, "param:parse: unknown key type \"" .. key .. "\""
+ else
+ error("type " .. typ .. " is not yet implemented")
+ end
+ end
+ -- for GC
+ buf_t[buf_param_key] = nil
+
+ return buf_t
+end
+
+local param_type_readable = {
+ [OSSL_PARAM_UNSIGNED_INTEGER] = "unsigned integer",
+ [OSSL_PARAM_INTEGER] = "integer",
+ [OSSL_PARAM_REAL] = "real number",
+ [OSSL_PARAM_UTF8_PTR] = "pointer to a UTF8 encoded string",
+ [OSSL_PARAM_UTF8_STRING] = "UTF8 encoded string",
+ [OSSL_PARAM_OCTET_PTR] = "pointer to an octet string",
+ [OSSL_PARAM_OCTET_STRING] = "octet string",
+}
+
+local function readable_data_type(p)
+ local typ = p.data_type
+ local literal = param_type_readable[typ]
+ if not literal then
+ literal = string.format("unknown type [%d]", typ)
+ end
+
+ local sz = tonumber(p.data_size)
+ if sz == 0 then
+ literal = literal .. " (arbitrary size)"
+ else
+ literal = literal .. string.format(" (max %d bytes large)", sz)
+ end
+ return literal
+end
+
+local function parse_params_schema(params, schema, schema_readable)
+ if params == nil then
+ return nil, format_error("parse_params_schema")
+ end
+
+ local i = 0
+ while true do
+ local p = params[i]
+ if p.key == nil then
+ break
+ end
+ local key = ffi_str(p.key)
+ if schema then
+ -- TODO: don't support same key with different types for now
+ -- prefer string type over integer types
+ local typ = tonumber(p.data_type)
+ if schema[key] then
+ schema[alter_type_key] = schema[alter_type_key] or {}
+ schema[alter_type_key][key] = typ
+ else
+ schema[key] = typ
+ end
+ end
+ -- if schema_return_size then -- only non-ptr string types are needed actually
+ -- schema_return_size[key] = tonumber(p.return_size)
+ -- end
+ if schema_readable then
+ table.insert(schema_readable, { key, readable_data_type(p) })
+ end
+ i = i + 1
+ end
+ return schema
+end
+
+local param_maps_set, param_maps_get = {}, {}
+
+local function get_params_func(typ, field)
+ local typ_lower = typ:sub(5):lower()
+ if typ_lower:sub(-4) == "_ctx" then
+ typ_lower = typ_lower:sub(0, -5)
+ end
+ -- field name for indexing schema, usually the (const) one created by
+ -- EVP_TYP_fetch or EVP_get_typebynam,e
+ field = field or "algo"
+
+ local cf_settable = C[typ .. "_settable_params"]
+ local settable = function(self, raw)
+ local k = self[field]
+ if raw and param_maps_set[k] then
+ return param_maps_set[k]
+ end
+
+ local param = cf_settable(self.ctx)
+ -- no params, this is fine, shouldn't be regarded as an error
+ if param == nil then
+ param_maps_set[k] = {}
+ return {}
+ end
+ local schema, schema_reabale = {}, raw and nil or {}
+ parse_params_schema(param, schema, schema_reabale)
+ param_maps_set[k] = schema
+
+ return raw and schema or schema_reabale
+ end
+
+ local cf_set = C[typ .. "_set_params"]
+ local set = function(self, params)
+ if not param_maps_set[self[field]] then
+ local ok, err = self:settable_params()
+ if not ok then
+ return false, typ_lower .. ":set_params: " .. err
+ end
+ end
+
+ local oparams, err = construct(params, nil, param_maps_set[self[field]])
+ if err then
+ return false, typ_lower .. ":set_params: " .. err
+ end
+
+ if cf_set(self.ctx, oparams) ~= 1 then
+ return false, format_error(typ_lower .. ":set_params: " .. typ .. "_set_params")
+ end
+
+ return true
+ end
+
+ local cf_gettable = C[typ .. "_gettable_params"]
+ local gettable = function(self, raw)
+ local k = self[field]
+ if raw and param_maps_set[k] then
+ return param_maps_set[k]
+ end
+
+ local param = cf_gettable(self.ctx)
+ -- no params, this is fine, shouldn't be regarded as an error
+ if param == nil then
+ param_maps_get[k] = {}
+ return {}
+ end
+ local schema, schema_reabale = {}, raw and nil or {}
+ parse_params_schema(param, schema, schema_reabale)
+ param_maps_set[k] = schema
+
+ return raw and schema or schema_reabale
+ end
+
+ local cf_get = C[typ .. "_get_params"]
+ local get_buffer, get_size_map = {}, {}
+ local get = function(self, key, want_size, want_type)
+ if not param_maps_get[self[field]] then
+ local ok, err = self:gettable_params()
+ if not ok then
+ return false, typ_lower .. ":set_params: " .. err
+ end
+ end
+ local schema = param_maps_set[self[field]]
+ if schema == nil or not schema[key] then -- nil or null
+ return nil, typ_lower .. ":get_param: unknown key \"" .. key .. "\""
+ end
+
+ table.clear(get_buffer)
+ table.clear(get_size_map)
+ get_buffer[key] = null
+ get_size_map[key] = want_size
+ schema = want_type and { [key] = want_type } or schema
+
+ local req, err = construct(get_buffer, 1, schema, get_size_map)
+ if not req then
+ return nil, typ_lower .. ":get_param: failed to construct params: " .. err
+ end
+
+ if cf_get(self.ctx, req) ~= 1 then
+ return nil, format_error(typ_lower .. ":get_param:get")
+ end
+
+ get_buffer, err = parse(get_buffer, 1, schema, get_size_map)
+ if err then
+ return nil, typ_lower .. ":get_param: failed to parse params: " .. err
+ end
+
+ return get_buffer[key]
+ end
+
+ return settable, set, gettable, get
+end
+
+return {
+ OSSL_PARAM_INTEGER = OSSL_PARAM_INTEGER,
+ OSSL_PARAM_UNSIGNED_INTEGER = OSSL_PARAM_INTEGER,
+ OSSL_PARAM_REAL = OSSL_PARAM_REAL,
+ OSSL_PARAM_UTF8_STRING = OSSL_PARAM_UTF8_STRING,
+ OSSL_PARAM_OCTET_STRING = OSSL_PARAM_OCTET_STRING,
+ OSSL_PARAM_UTF8_PTR = OSSL_PARAM_UTF8_PTR,
+ OSSL_PARAM_OCTET_PTR = OSSL_PARAM_OCTET_PTR,
+
+ construct = construct,
+ parse = parse,
+ parse_params_schema = parse_params_schema,
+ get_params_func = get_params_func,
+} \ No newline at end of file
diff --git a/server/resty/openssl/pkcs12.lua b/server/resty/openssl/pkcs12.lua
new file mode 100644
index 0000000..6e3b216
--- /dev/null
+++ b/server/resty/openssl/pkcs12.lua
@@ -0,0 +1,168 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.pkcs12"
+require "resty.openssl.include.bio"
+local bio_util = require "resty.openssl.auxiliary.bio"
+local format_error = require("resty.openssl.err").format_error
+local pkey_lib = require "resty.openssl.pkey"
+local x509_lib = require "resty.openssl.x509"
+local stack_macro = require "resty.openssl.include.stack"
+local stack_lib = require "resty.openssl.stack"
+local objects_lib = require "resty.openssl.objects"
+local ctx_lib = require "resty.openssl.ctx"
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local stack_of_x509_new = stack_lib.new_of("X509")
+local stack_of_x509_add = stack_lib.add_of("X509")
+local stack_of_x509_iter = stack_lib.mt_of("X509", x509_lib.dup, {}).__ipairs
+
+local ptr_ptr_of_pkey = ffi.typeof("EVP_PKEY*[1]")
+local ptr_ptr_of_x509 = ffi.typeof("X509*[1]")
+local ptr_ptr_of_stack = ffi.typeof("OPENSSL_STACK*[1]")
+
+local function decode(p12, passphrase)
+ local bio = C.BIO_new_mem_buf(p12, #p12)
+ if bio == nil then
+ return nil, "pkcs12.decode: BIO_new_mem_buf() failed"
+ end
+ ffi_gc(bio, C.BIO_free)
+
+ local p12 = C.d2i_PKCS12_bio(bio, nil)
+ if p12 == nil then
+ return nil, format_error("pkcs12.decode: d2i_PKCS12_bio")
+ end
+ ffi_gc(p12, C.PKCS12_free)
+
+ local ppkey = ptr_ptr_of_pkey()
+ local px509 = ptr_ptr_of_x509()
+ local pstack = ptr_ptr_of_stack()
+ local stack = stack_of_x509_new()
+ -- assign a valid OPENSSL_STACK so gc is taken care of
+ pstack[0] = stack
+
+ local code = C.PKCS12_parse(p12, passphrase or "", ppkey, px509, pstack)
+ if code ~= 1 then
+ return nil, format_error("pkcs12.decode: PKCS12_parse")
+ end
+
+ local cacerts
+ local n = stack_macro.OPENSSL_sk_num(stack)
+ if n > 0 then
+ cacerts = {}
+ local iter = stack_of_x509_iter({ ctx = stack })
+ for i=1, n do
+ local _, c = iter()
+ cacerts[i] = c
+ end
+ end
+
+ local friendly_name = C.X509_alias_get0(px509[0], nil)
+ if friendly_name ~= nil then
+ friendly_name = ffi_str(friendly_name)
+ end
+
+ return {
+ key = pkey_lib.new(ppkey[0]),
+ cert = x509_lib.new(px509[0]),
+ friendly_name = friendly_name,
+ cacerts = cacerts,
+ -- store reference to the stack, so it's not GC'ed unexpectedly
+ _stack = stack,
+ }
+end
+
+local function encode(opts, passphrase, properties)
+ if passphrase and type(passphrase) ~= "string" then
+ return nil, "pkcs12.encode: expect passphrase to be a string"
+ end
+ local pkey = opts.key
+ if not pkey_lib.istype(pkey) then
+ return nil, "pkcs12.encode: expect key to be a pkey instance"
+ end
+ local cert = opts.cert
+ if not x509_lib.istype(cert) then
+ return nil, "pkcs12.encode: expect cert to be a x509 instance"
+ end
+
+ local ok, err = cert:check_private_key(pkey)
+ if not ok then
+ return nil, "pkcs12.encode: key doesn't match cert: " .. err
+ end
+
+ local nid_key = opts.nid_key
+ if nid_key then
+ nid_key, err = objects_lib.txtnid2nid(nid_key)
+ if err then
+ return nil, "pkcs12.encode: invalid nid_key"
+ end
+ end
+
+ local nid_cert = opts.nid_cert
+ if nid_cert then
+ nid_cert, err = objects_lib.txtnid2nid(nid_cert)
+ if err then
+ return nil, "pkcs12.encode: invalid nid_cert"
+ end
+ end
+
+ local x509stack
+ local cacerts = opts.cacerts
+ if cacerts then
+ if type(cacerts) ~= "table" then
+ return nil, "pkcs12.encode: expect cacerts to be a table"
+ end
+ if #cacerts > 0 then
+ -- stack lib handles gc
+ x509stack = stack_of_x509_new()
+ for _, c in ipairs(cacerts) do
+ if not OPENSSL_10 then
+ if C.X509_up_ref(c.ctx) ~= 1 then
+ return nil, "pkcs12.encode: failed to add cacerts: X509_up_ref failed"
+ end
+ end
+ local ok, err = stack_of_x509_add(x509stack, c.ctx)
+ if not ok then
+ return nil, "pkcs12.encode: failed to add cacerts: " .. err
+ end
+ end
+ if OPENSSL_10 then
+ -- OpenSSL 1.0.2 doesn't have X509_up_ref
+ -- shallow copy the stack, up_ref for each element
+ x509stack = C.X509_chain_up_ref(x509stack)
+ -- use the shallow gc
+ ffi_gc(x509stack, stack_macro.OPENSSL_sk_free)
+ end
+ end
+ end
+
+ local p12
+ if OPENSSL_3X then
+ p12 = C.PKCS12_create_ex(passphrase or "", opts.friendly_name,
+ pkey.ctx, cert.ctx, x509stack,
+ nid_key or 0, nid_cert or 0,
+ opts.iter or 0, opts.mac_iter or 0, 0,
+ ctx_lib.get_libctx(), properties)
+ else
+ p12 = C.PKCS12_create(passphrase or "", opts.friendly_name,
+ pkey.ctx, cert.ctx, x509stack,
+ nid_key or 0, nid_cert or 0,
+ opts.iter or 0, opts.mac_iter or 0, 0)
+ end
+ if p12 == nil then
+ return nil, format_error("pkcs12.encode: PKCS12_create")
+ end
+ ffi_gc(p12, C.PKCS12_free)
+
+ return bio_util.read_wrap(C.i2d_PKCS12_bio, p12)
+end
+
+return {
+ decode = decode,
+ loads = decode,
+ encode = encode,
+ dumps = encode,
+} \ No newline at end of file
diff --git a/server/resty/openssl/pkey.lua b/server/resty/openssl/pkey.lua
new file mode 100644
index 0000000..69c5aae
--- /dev/null
+++ b/server/resty/openssl/pkey.lua
@@ -0,0 +1,942 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_new = ffi.new
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+local ffi_copy = ffi.copy
+
+local rsa_macro = require "resty.openssl.include.rsa"
+local dh_macro = require "resty.openssl.include.dh"
+require "resty.openssl.include.bio"
+require "resty.openssl.include.pem"
+require "resty.openssl.include.x509"
+require "resty.openssl.include.evp.pkey"
+local evp_macro = require "resty.openssl.include.evp"
+local pkey_macro = require "resty.openssl.include.evp.pkey"
+local bio_util = require "resty.openssl.auxiliary.bio"
+local digest_lib = require "resty.openssl.digest"
+local rsa_lib = require "resty.openssl.rsa"
+local dh_lib = require "resty.openssl.dh"
+local ec_lib = require "resty.openssl.ec"
+local ecx_lib = require "resty.openssl.ecx"
+local objects_lib = require "resty.openssl.objects"
+local jwk_lib = require "resty.openssl.auxiliary.jwk"
+local ctx_lib = require "resty.openssl.ctx"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local OPENSSL_111_OR_LATER = require("resty.openssl.version").OPENSSL_111_OR_LATER
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local ptr_of_uint = ctypes.ptr_of_uint
+local ptr_of_size_t = ctypes.ptr_of_size_t
+local ptr_of_int = ctypes.ptr_of_int
+
+local null = ctypes.null
+local load_pem_args = { null, null, null }
+local load_der_args = { null }
+
+local get_pkey_key
+if OPENSSL_11_OR_LATER then
+ get_pkey_key = {
+ [evp_macro.EVP_PKEY_RSA] = function(ctx) return C.EVP_PKEY_get0_RSA(ctx) end,
+ [evp_macro.EVP_PKEY_EC] = function(ctx) return C.EVP_PKEY_get0_EC_KEY(ctx) end,
+ [evp_macro.EVP_PKEY_DH] = function(ctx) return C.EVP_PKEY_get0_DH(ctx) end
+ }
+else
+ get_pkey_key = {
+ [evp_macro.EVP_PKEY_RSA] = function(ctx) return ctx.pkey and ctx.pkey.rsa end,
+ [evp_macro.EVP_PKEY_EC] = function(ctx) return ctx.pkey and ctx.pkey.ec end,
+ [evp_macro.EVP_PKEY_DH] = function(ctx) return ctx.pkey and ctx.pkey.dh end,
+ }
+end
+
+local load_rsa_key_funcs
+
+if not OPENSSL_3X then
+ load_rsa_key_funcs= {
+ ['PEM_read_bio_RSAPrivateKey'] = true,
+ ['PEM_read_bio_RSAPublicKey'] = true,
+ } -- those functions return RSA* instead of EVP_PKEY*
+end
+
+local function load_pem_der(txt, opts, funcs)
+ local fmt = opts.format or '*'
+ if fmt ~= 'PEM' and fmt ~= 'DER' and fmt ~= "JWK" and fmt ~= '*' then
+ return nil, "expecting 'DER', 'PEM', 'JWK' or '*' as \"format\""
+ end
+
+ local typ = opts.type or '*'
+ if typ ~= 'pu' and typ ~= 'pr' and typ ~= '*' then
+ return nil, "expecting 'pr', 'pu' or '*' as \"type\""
+ end
+
+ if fmt == "JWK" and (typ == "pu" or type == "pr") then
+ return nil, "explictly load private or public key from JWK format is not supported"
+ end
+
+ ngx.log(ngx.DEBUG, "load key using fmt: ", fmt, ", type: ", typ)
+
+ local bio = C.BIO_new_mem_buf(txt, #txt)
+ if bio == nil then
+ return nil, "BIO_new_mem_buf() failed"
+ end
+ ffi_gc(bio, C.BIO_free)
+
+ local ctx
+
+ local fs = funcs[fmt][typ]
+ local passphrase_cb
+ for f, arg in pairs(fs) do
+ -- don't need BIO when loading JWK key: we parse it in Lua land
+ if f == "load_jwk" then
+ local err
+ ctx, err = jwk_lib[f](txt)
+ if ctx == nil then
+ -- if fmt is explictly set to JWK, we should return an error now
+ if fmt == "JWK" then
+ return nil, err
+ end
+ ngx.log(ngx.DEBUG, "jwk decode failed: ", err, ", continuing")
+ end
+ else
+ -- #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "BIO_ctrl() failed"
+ end
+
+ -- only pass in passphrase/passphrase_cb to PEM_* functions
+ if fmt == "PEM" or (fmt == "*" and arg == load_pem_args) then
+ if opts.passphrase then
+ local passphrase = opts.passphrase
+ if type(passphrase) ~= "string" then
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ return nil, "passphrase must be a string"
+ end
+ arg = { null, nil, passphrase }
+ elseif opts.passphrase_cb then
+ passphrase_cb = passphrase_cb or ffi_cast("pem_password_cb", function(buf, size)
+ local p = opts.passphrase_cb()
+ local len = #p -- 1 byte for \0
+ if len > size then
+ ngx.log(ngx.WARN, "pkey:load_pem_der: passphrase truncated from ", len, " to ", size)
+ len = size
+ end
+ ffi_copy(buf, p, len)
+ return len
+ end)
+ arg = { null, passphrase_cb, null }
+ end
+ end
+
+ ctx = C[f](bio, unpack(arg))
+ end
+
+ if ctx ~= nil then
+ ngx.log(ngx.DEBUG, "pkey:load_pem_der: loaded pkey using function ", f)
+
+ -- pkcs1 functions create a rsa rather than evp_pkey
+ -- disable the checking in openssl 3.0 for sail safe
+ if not OPENSSL_3X and load_rsa_key_funcs[f] then
+ local rsa = ctx
+ ctx = C.EVP_PKEY_new()
+ if ctx == null then
+ return nil, format_error("pkey:load_pem_der: EVP_PKEY_new")
+ end
+
+ if C.EVP_PKEY_assign(ctx, evp_macro.EVP_PKEY_RSA, rsa) ~= 1 then
+ C.RSA_free(rsa)
+ C.EVP_PKEY_free(ctx)
+ return nil, "pkey:load_pem_der: EVP_PKEY_assign() failed"
+ end
+ end
+
+ break
+ end
+ end
+ if passphrase_cb ~= nil then
+ passphrase_cb:free()
+ end
+
+ if ctx == nil then
+ return nil, format_error()
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ return ctx, nil
+end
+
+local function generate_param(key_type, config)
+ if key_type == evp_macro.EVP_PKEY_DH then
+ local dh_group = config.group
+ if dh_group then
+ local get_group_func = dh_macro.dh_groups[dh_group]
+ if not get_group_func then
+ return nil, "unknown pre-defined group " .. dh_group
+ end
+ local ctx = get_group_func()
+ if ctx == nil then
+ return nil, format_error("DH_get_x")
+ end
+ local params = C.EVP_PKEY_new()
+ if not params then
+ return nil, format_error("EVP_PKEY_new")
+ end
+ ffi_gc(params, C.EVP_PKEY_free)
+ if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
+ return nil, format_error("EVP_PKEY_assign")
+ end
+ return params
+ end
+ end
+
+ local pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
+ if pctx == nil then
+ return nil, format_error("EVP_PKEY_CTX_new_id")
+ end
+ ffi_gc(pctx, C.EVP_PKEY_CTX_free)
+
+ if C.EVP_PKEY_paramgen_init(pctx) ~= 1 then
+ return nil, format_error("EVP_PKEY_paramgen_init")
+ end
+
+ if key_type == evp_macro.EVP_PKEY_EC then
+ local curve = config.curve or 'prime192v1'
+ local nid = C.OBJ_ln2nid(curve)
+ if nid == 0 then
+ return nil, "unknown curve " .. curve
+ end
+ if pkey_macro.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 then
+ return nil, format_error("EVP_PKEY_CTX_ctrl: EC: curve_nid")
+ end
+ if not BORINGSSL then
+ -- use the named-curve encoding for best backward-compatibilty
+ -- and for playing well with go:crypto/x509
+ -- # define OPENSSL_EC_NAMED_CURVE 0x001
+ if pkey_macro.EVP_PKEY_CTX_set_ec_param_enc(pctx, 1) <= 0 then
+ return nil, format_error("EVP_PKEY_CTX_ctrl: EC: param_enc")
+ end
+ end
+ elseif key_type == evp_macro.EVP_PKEY_DH then
+ local bits = config.bits
+ if not config.param and not bits then
+ bits = 2048
+ end
+ if bits and pkey_macro.EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx, bits) <= 0 then
+ return nil, format_error("EVP_PKEY_CTX_ctrl: DH: bits")
+ end
+ end
+
+ local ctx_ptr = ffi_new("EVP_PKEY*[1]")
+ if C.EVP_PKEY_paramgen(pctx, ctx_ptr) ~= 1 then
+ return nil, format_error("EVP_PKEY_paramgen")
+ end
+
+ local params = ctx_ptr[0]
+ ffi_gc(params, C.EVP_PKEY_free)
+
+ return params
+end
+
+local load_param_funcs = {
+ [evp_macro.EVP_PKEY_EC] = {
+ ["*"] = {
+ ["*"] = {
+ ['PEM_read_bio_ECPKParameters'] = load_pem_args,
+ -- ['d2i_ECPKParameters_bio'] = load_der_args,
+ }
+ },
+ },
+ [evp_macro.EVP_PKEY_DH] = {
+ ["*"] = {
+ ["*"] = {
+ ['PEM_read_bio_DHparams'] = load_pem_args,
+ -- ['d2i_DHparams_bio'] = load_der_args,
+ }
+ },
+ },
+}
+
+local function generate_key(config)
+ local typ = config.type or 'RSA'
+ local key_type
+
+ if typ == "RSA" then
+ key_type = evp_macro.EVP_PKEY_RSA
+ elseif typ == "EC" then
+ key_type = evp_macro.EVP_PKEY_EC
+ elseif typ == "DH" then
+ key_type = evp_macro.EVP_PKEY_DH
+ elseif evp_macro.ecx_curves[typ] then
+ key_type = evp_macro.ecx_curves[typ]
+ else
+ return nil, "unsupported type " .. typ
+ end
+ if key_type == 0 then
+ return nil, "the linked OpenSSL library doesn't support " .. typ .. " key"
+ end
+
+ local pctx
+
+ if key_type == evp_macro.EVP_PKEY_EC or key_type == evp_macro.EVP_PKEY_DH then
+ local params, err
+ if config.param then
+ -- HACK
+ config.type = nil
+ local ctx, err = load_pem_der(config.param, config, load_param_funcs[key_type])
+ if err then
+ return nil, "load_pem_der: " .. err
+ end
+ if key_type == evp_macro.EVP_PKEY_EC then
+ local ec_group = ctx
+ ffi_gc(ec_group, C.EC_GROUP_free)
+ ctx = C.EC_KEY_new()
+ if ctx == nil then
+ return nil, "EC_KEY_new() failed"
+ end
+ if C.EC_KEY_set_group(ctx, ec_group) ~= 1 then
+ return nil, format_error("EC_KEY_set_group")
+ end
+ end
+ params = C.EVP_PKEY_new()
+ if not params then
+ return nil, format_error("EVP_PKEY_new")
+ end
+ ffi_gc(params, C.EVP_PKEY_free)
+ if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
+ return nil, format_error("EVP_PKEY_assign")
+ end
+ else
+ params, err = generate_param(key_type, config)
+ if err then
+ return nil, "generate_param: " .. err
+ end
+ end
+ pctx = C.EVP_PKEY_CTX_new(params, nil)
+ if pctx == nil then
+ return nil, format_error("EVP_PKEY_CTX_new")
+ end
+ else
+ pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
+ if pctx == nil then
+ return nil, format_error("EVP_PKEY_CTX_new_id")
+ end
+ end
+
+ ffi_gc(pctx, C.EVP_PKEY_CTX_free)
+
+ if C.EVP_PKEY_keygen_init(pctx) ~= 1 then
+ return nil, format_error("EVP_PKEY_keygen_init")
+ end
+ -- RSA key parameters are set for keygen ctx not paramgen
+ if key_type == evp_macro.EVP_PKEY_RSA then
+ local bits = config.bits or 2048
+ if bits > 4294967295 then
+ return nil, "bits out of range"
+ end
+
+ if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, bits) <= 0 then
+ return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: bits")
+ end
+
+ if config.exp then
+ -- don't free exp as it's used internally in key
+ local exp = C.BN_new()
+ if exp == nil then
+ return nil, "BN_new() failed"
+ end
+ C.BN_set_word(exp, config.exp)
+
+ if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_pubexp(pctx, exp) <= 0 then
+ return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: exp")
+ end
+ end
+ end
+ local ctx_ptr = ffi_new("EVP_PKEY*[1]")
+ -- TODO: move to use EVP_PKEY_gen after drop support for <1.1.1
+ if C.EVP_PKEY_keygen(pctx, ctx_ptr) ~= 1 then
+ return nil, format_error("EVP_PKEY_gen")
+ end
+ return ctx_ptr[0]
+end
+
+local load_key_try_funcs = {} do
+ -- TODO: pkcs1 load functions are not required in openssl 3.0
+ local _load_key_try_funcs = {
+ PEM = {
+ -- Note: make sure we always try load priv key first
+ pr = {
+ ['PEM_read_bio_PrivateKey'] = load_pem_args,
+ -- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
+ ['PEM_read_bio_RSAPrivateKey'] = not OPENSSL_3X and load_pem_args or nil,
+ },
+ pu = {
+ ['PEM_read_bio_PUBKEY'] = load_pem_args,
+ -- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
+ ['PEM_read_bio_RSAPublicKey'] = not OPENSSL_3X and load_pem_args or nil,
+ },
+ },
+ DER = {
+ pr = { ['d2i_PrivateKey_bio'] = load_der_args, },
+ pu = { ['d2i_PUBKEY_bio'] = load_der_args, },
+ },
+ JWK = {
+ pr = { ['load_jwk'] = {}, },
+ }
+ }
+ -- populate * funcs
+ local all_funcs = {}
+ local typ_funcs = {}
+ for fmt, ffs in pairs(_load_key_try_funcs) do
+ load_key_try_funcs[fmt] = ffs
+
+ local funcs = {}
+ for typ, fs in pairs(ffs) do
+ for f, arg in pairs(fs) do
+ funcs[f] = arg
+ all_funcs[f] = arg
+ if not typ_funcs[typ] then
+ typ_funcs[typ] = {}
+ end
+ typ_funcs[typ] = arg
+ end
+ end
+ load_key_try_funcs[fmt]["*"] = funcs
+ end
+ load_key_try_funcs["*"] = {}
+ load_key_try_funcs["*"]["*"] = all_funcs
+ for typ, fs in pairs(typ_funcs) do
+ load_key_try_funcs[typ] = fs
+ end
+end
+
+local function __tostring(self, is_priv, fmt, is_pkcs1)
+ if fmt == "JWK" then
+ return jwk_lib.dump_jwk(self, is_priv)
+ elseif is_pkcs1 then
+ if fmt ~= "PEM" or self.key_type ~= evp_macro.EVP_PKEY_RSA then
+ return nil, "PKCS#1 format is only supported to encode RSA key in \"PEM\" format"
+ elseif OPENSSL_3X then -- maybe possible with OSSL_ENCODER_CTX_new_for_pkey though
+ return nil, "writing out RSA key in PKCS#1 format is not supported in OpenSSL 3.0"
+ end
+ end
+ if is_priv then
+ if fmt == "DER" then
+ return bio_util.read_wrap(C.i2d_PrivateKey_bio, self.ctx)
+ end
+ -- PEM
+ if is_pkcs1 then
+ local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
+ if rsa == nil then
+ return nil, "unable to read RSA key for writing"
+ end
+ return bio_util.read_wrap(C.PEM_write_bio_RSAPrivateKey,
+ rsa,
+ nil, nil, 0, nil, nil)
+ end
+ return bio_util.read_wrap(C.PEM_write_bio_PrivateKey,
+ self.ctx,
+ nil, nil, 0, nil, nil)
+ else
+ if fmt == "DER" then
+ return bio_util.read_wrap(C.i2d_PUBKEY_bio, self.ctx)
+ end
+ -- PEM
+ if is_pkcs1 then
+ local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
+ if rsa == nil then
+ return nil, "unable to read RSA key for writing"
+ end
+ return bio_util.read_wrap(C.PEM_write_bio_RSAPublicKey, rsa)
+ end
+ return bio_util.read_wrap(C.PEM_write_bio_PUBKEY, self.ctx)
+ end
+
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+local empty_table = {}
+local evp_pkey_ptr_ct = ffi.typeof('EVP_PKEY*')
+
+function _M.new(s, opts)
+ local ctx, err
+ s = s or {}
+ if type(s) == 'table' then
+ ctx, err = generate_key(s)
+ if err then
+ err = "pkey.new:generate_key: " .. err
+ end
+ elseif type(s) == 'string' then
+ ctx, err = load_pem_der(s, opts or empty_table, load_key_try_funcs)
+ if err then
+ err = "pkey.new:load_key: " .. err
+ end
+ elseif type(s) == 'cdata' then
+ if ffi.istype(evp_pkey_ptr_ct, s) then
+ ctx = s
+ else
+ return nil, "pkey.new: expect a EVP_PKEY* cdata at #1"
+ end
+ else
+ return nil, "pkey.new: unexpected type " .. type(s) .. " at #1"
+ end
+
+ if err then
+ return nil, err
+ end
+
+ ffi_gc(ctx, C.EVP_PKEY_free)
+
+ local key_type = OPENSSL_3X and C.EVP_PKEY_get_base_id(ctx) or C.EVP_PKEY_base_id(ctx)
+ if key_type == 0 then
+ return nil, "pkey.new: cannot get key_type"
+ end
+ local key_type_is_ecx = (key_type == evp_macro.EVP_PKEY_ED25519) or
+ (key_type == evp_macro.EVP_PKEY_X25519) or
+ (key_type == evp_macro.EVP_PKEY_ED448) or
+ (key_type == evp_macro.EVP_PKEY_X448)
+
+ -- although OpenSSL discourages to use this size for digest/verify
+ -- but this is good enough for now
+ local buf_size = OPENSSL_3X and C.EVP_PKEY_get_size(ctx) or C.EVP_PKEY_size(ctx)
+
+ local self = setmetatable({
+ ctx = ctx,
+ pkey_ctx = nil,
+ rsa_padding = nil,
+ key_type = key_type,
+ key_type_is_ecx = key_type_is_ecx,
+ buf = ctypes.uchar_array(buf_size),
+ buf_size = buf_size,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(evp_pkey_ptr_ct, l.ctx)
+end
+
+function _M:get_key_type()
+ return objects_lib.nid2table(self.key_type)
+end
+
+function _M:get_default_digest_type()
+ if BORINGSSL then
+ return nil, "BoringSSL doesn't have default digest for pkey"
+ end
+
+ local nid = ptr_of_int()
+ local code = C.EVP_PKEY_get_default_digest_nid(self.ctx, nid)
+ if code == -2 then
+ return nil, "operation is not supported by the public key algorithm"
+ elseif code <= 0 then
+ return nil, format_error("get_default_digest", code)
+ end
+
+ local ret = objects_lib.nid2table(nid[0])
+ ret.mandatory = code == 2
+ return ret
+end
+
+function _M:get_provider_name()
+ if not OPENSSL_3X then
+ return false, "pkey:get_provider_name is not supported"
+ end
+ local p = C.EVP_PKEY_get0_provider(self.ctx)
+ if p == nil then
+ return nil
+ end
+ return ffi_str(C.OSSL_PROVIDER_get0_name(p))
+end
+
+if OPENSSL_3X then
+ local param_lib = require "resty.openssl.param"
+ _M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_PKEY", "key_type")
+end
+
+function _M:get_parameters()
+ if not self.key_type_is_ecx then
+ local getter = get_pkey_key[self.key_type]
+ if not getter then
+ return nil, "key getter not defined"
+ end
+ local key = getter(self.ctx)
+ if key == nil then
+ return nil, format_error("EVP_PKEY_get0_{key}")
+ end
+
+ if self.key_type == evp_macro.EVP_PKEY_RSA then
+ return rsa_lib.get_parameters(key)
+ elseif self.key_type == evp_macro.EVP_PKEY_EC then
+ return ec_lib.get_parameters(key)
+ elseif self.key_type == evp_macro.EVP_PKEY_DH then
+ return dh_lib.get_parameters(key)
+ end
+ else
+ return ecx_lib.get_parameters(self.ctx)
+ end
+end
+
+function _M:set_parameters(opts)
+ if not self.key_type_is_ecx then
+ local getter = get_pkey_key[self.key_type]
+ if not getter then
+ return nil, "key getter not defined"
+ end
+ local key = getter(self.ctx)
+ if key == nil then
+ return nil, format_error("EVP_PKEY_get0_{key}")
+ end
+
+ if self.key_type == evp_macro.EVP_PKEY_RSA then
+ return rsa_lib.set_parameters(key, opts)
+ elseif self.key_type == evp_macro.EVP_PKEY_EC then
+ return ec_lib.set_parameters(key, opts)
+ elseif self.key_type == evp_macro.EVP_PKEY_DH then
+ return dh_lib.set_parameters(key, opts)
+ end
+ else
+ -- for ecx keys we always create a new EVP_PKEY and release the old one
+ local ctx, err = ecx_lib.set_parameters(self.key_type, self.ctx, opts)
+ if err then
+ return false, err
+ end
+ self.ctx = ctx
+ end
+end
+
+function _M:is_private()
+ local params = self:get_parameters()
+ if self.key_type == evp_macro.EVP_PKEY_RSA then
+ return params.d ~= nil
+ else
+ return params.private ~= nil
+ end
+end
+
+local ASYMMETRIC_OP_ENCRYPT = 0x1
+local ASYMMETRIC_OP_DECRYPT = 0x2
+local ASYMMETRIC_OP_SIGN_RAW = 0x4
+local ASYMMETRIC_OP_VERIFY_RECOVER = 0x8
+
+local function asymmetric_routine(self, s, op, padding)
+ local pkey_ctx
+
+ if self.key_type == evp_macro.EVP_PKEY_RSA then
+ if padding then
+ padding = tonumber(padding)
+ if not padding then
+ return nil, "invalid padding: " .. __tostring(padding)
+ end
+ else
+ padding = rsa_macro.paddings.RSA_PKCS1_PADDING
+ end
+ end
+
+ if self.pkey_ctx ~= nil and
+ (self.key_type ~= evp_macro.EVP_PKEY_RSA or self.rsa_padding == padding) then
+ pkey_ctx = self.pkey_ctx
+ else
+ pkey_ctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
+ if pkey_ctx == nil then
+ return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_new()")
+ end
+ ffi_gc(pkey_ctx, C.EVP_PKEY_CTX_free)
+ self.pkey_ctx = pkey_ctx
+ end
+
+ local f, fint, op_name
+ if op == ASYMMETRIC_OP_ENCRYPT then
+ fint = C.EVP_PKEY_encrypt_init
+ f = C.EVP_PKEY_encrypt
+ op_name = "encrypt"
+ elseif op == ASYMMETRIC_OP_DECRYPT then
+ fint = C.EVP_PKEY_decrypt_init
+ f = C.EVP_PKEY_decrypt
+ op_name = "decrypt"
+ elseif op == ASYMMETRIC_OP_SIGN_RAW then
+ fint = C.EVP_PKEY_sign_init
+ f = C.EVP_PKEY_sign
+ op_name = "sign"
+ elseif op == ASYMMETRIC_OP_VERIFY_RECOVER then
+ fint = C.EVP_PKEY_verify_recover_init
+ f = C.EVP_PKEY_verify_recover
+ op_name = "verify_recover"
+ else
+ error("bad \"op\", got " .. op, 2)
+ end
+
+ local code = fint(pkey_ctx)
+ if code < 1 then
+ return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name .. "_init", code)
+ end
+
+ -- EVP_PKEY_CTX_ctrl must be called after *_init
+ if self.key_type == evp_macro.EVP_PKEY_RSA and padding then
+ if pkey_macro.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding) ~= 1 then
+ return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_set_rsa_padding")
+ end
+ self.rsa_padding = padding
+ end
+
+ local length = ptr_of_size_t(self.buf_size)
+
+ if f(pkey_ctx, self.buf, length, s, #s) <= 0 then
+ return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name)
+ end
+
+ return ffi_str(self.buf, length[0]), nil
+end
+
+_M.PADDINGS = rsa_macro.paddings
+
+function _M:encrypt(s, padding)
+ return asymmetric_routine(self, s, ASYMMETRIC_OP_ENCRYPT, padding)
+end
+
+function _M:decrypt(s, padding)
+ return asymmetric_routine(self, s, ASYMMETRIC_OP_DECRYPT, padding)
+end
+
+function _M:sign_raw(s, padding)
+ -- TODO: temporary hack before OpenSSL has proper check for existence of private key
+ if self.key_type_is_ecx and not self:is_private() then
+ return nil, "pkey:sign_raw: missing private key"
+ end
+
+ return asymmetric_routine(self, s, ASYMMETRIC_OP_SIGN_RAW, padding)
+end
+
+function _M:verify_recover(s, padding)
+ return asymmetric_routine(self, s, ASYMMETRIC_OP_VERIFY_RECOVER, padding)
+end
+
+local evp_pkey_ctx_ptr_ptr_ct = ffi.typeof('EVP_PKEY_CTX*[1]')
+
+local function sign_verify_prepare(self, fint, md_alg, padding, opts)
+ local pkey_ctx
+
+ if self.key_type == evp_macro.EVP_PKEY_RSA and padding then
+ pkey_ctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
+ if pkey_ctx == nil then
+ return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_new()")
+ end
+ ffi_gc(pkey_ctx, C.EVP_PKEY_CTX_free)
+ end
+
+ local md_ctx = C.EVP_MD_CTX_new()
+ if md_ctx == nil then
+ return nil, "pkey:sign_verify_prepare: EVP_MD_CTX_new() failed"
+ end
+ ffi_gc(md_ctx, C.EVP_MD_CTX_free)
+
+ local algo
+ if md_alg then
+ if OPENSSL_3X then
+ algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), md_alg, nil)
+ else
+ algo = C.EVP_get_digestbyname(md_alg)
+ end
+ if algo == nil then
+ return nil, string.format("pkey:sign_verify_prepare: invalid digest type \"%s\"", md_alg)
+ end
+ end
+
+ local ppkey_ctx = evp_pkey_ctx_ptr_ptr_ct()
+ ppkey_ctx[0] = pkey_ctx
+ if fint(md_ctx, ppkey_ctx, algo, nil, self.ctx) ~= 1 then
+ return nil, format_error("pkey:sign_verify_prepare: Init failed")
+ end
+
+ if self.key_type == evp_macro.EVP_PKEY_RSA then
+ if padding then
+ if pkey_macro.EVP_PKEY_CTX_set_rsa_padding(ppkey_ctx[0], padding) ~= 1 then
+ return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_set_rsa_padding")
+ end
+ end
+ if opts and opts.pss_saltlen and padding ~= rsa_macro.paddings.RSA_PKCS1_PSS_PADDING then
+ if pkey_macro.EVP_PKEY_CTX_set_rsa_pss_saltlen(ppkey_ctx[0], opts.pss_saltlen) ~= 1 then
+ return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_set_rsa_pss_saltlen")
+ end
+ end
+ end
+
+ return md_ctx
+end
+
+function _M:sign(digest, md_alg, padding, opts)
+ -- TODO: temporary hack before OpenSSL has proper check for existence of private key
+ if self.key_type_is_ecx and not self:is_private() then
+ return nil, "pkey:sign: missing private key"
+ end
+
+ if digest_lib.istype(digest) then
+ local length = ptr_of_uint()
+ if C.EVP_SignFinal(digest.ctx, self.buf, length, self.ctx) ~= 1 then
+ return nil, format_error("pkey:sign: EVP_SignFinal")
+ end
+ return ffi_str(self.buf, length[0]), nil
+ elseif type(digest) == "string" then
+ if not OPENSSL_111_OR_LATER and not BORINGSSL then
+ -- we can still support earilier version with *Update and *Final
+ -- but we choose to not relying on the legacy interface for simplicity
+ return nil, "pkey:sign: new-style sign only available in OpenSSL 1.1.1 (or BoringSSL 1.1.0) or later"
+ elseif BORINGSSL and not md_alg and not self.key_type_is_ecx then
+ return nil, "pkey:sign: BoringSSL doesn't provide default digest, md_alg must be specified"
+ end
+
+ local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestSignInit, md_alg, padding, opts)
+ if err then
+ return nil, err
+ end
+
+ local length = ptr_of_size_t(self.buf_size)
+ if C.EVP_DigestSign(md_ctx, self.buf, length, digest, #digest) ~= 1 then
+ return nil, format_error("pkey:sign: EVP_DigestSign")
+ end
+ return ffi_str(self.buf, length[0]), nil
+ else
+ return nil, "pkey:sign: expect a digest instance or a string at #1"
+ end
+end
+
+function _M:verify(signature, digest, md_alg, padding, opts)
+ if type(signature) ~= "string" then
+ return nil, "pkey:verify: expect a string at #1"
+ end
+
+ local code
+ if digest_lib.istype(digest) then
+ code = C.EVP_VerifyFinal(digest.ctx, signature, #signature, self.ctx)
+ elseif type(digest) == "string" then
+ if not OPENSSL_111_OR_LATER and not BORINGSSL then
+ -- we can still support earilier version with *Update and *Final
+ -- but we choose to not relying on the legacy interface for simplicity
+ return nil, "pkey:verify: new-style verify only available in OpenSSL 1.1.1 (or BoringSSL 1.1.0) or later"
+ elseif BORINGSSL and not md_alg and not self.key_type_is_ecx then
+ return nil, "pkey:verify: BoringSSL doesn't provide default digest, md_alg must be specified"
+ end
+
+ local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestVerifyInit, md_alg, padding, opts)
+ if err then
+ return nil, err
+ end
+
+ code = C.EVP_DigestVerify(md_ctx, signature, #signature, digest, #digest)
+ else
+ return nil, "pkey:verify: expect a digest instance or a string at #2"
+ end
+
+ if code == 0 then
+ return false, nil
+ elseif code == 1 then
+ return true, nil
+ end
+ return false, format_error("pkey:verify")
+end
+
+function _M:derive(peerkey)
+ if not self.istype(peerkey) then
+ return nil, "pkey:derive: expect a pkey instance at #1"
+ end
+ local pctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
+ if pctx == nil then
+ return nil, "pkey:derive: EVP_PKEY_CTX_new() failed"
+ end
+ ffi_gc(pctx, C.EVP_PKEY_CTX_free)
+ local code = C.EVP_PKEY_derive_init(pctx)
+ if code <= 0 then
+ return nil, format_error("pkey:derive: EVP_PKEY_derive_init", code)
+ end
+
+ code = C.EVP_PKEY_derive_set_peer(pctx, peerkey.ctx)
+ if code <= 0 then
+ return nil, format_error("pkey:derive: EVP_PKEY_derive_set_peer", code)
+ end
+
+ local buflen = ptr_of_size_t()
+ code = C.EVP_PKEY_derive(pctx, nil, buflen)
+ if code <= 0 then
+ return nil, format_error("pkey:derive: EVP_PKEY_derive check buffer size", code)
+ end
+
+ local buf = ctypes.uchar_array(buflen[0])
+ code = C.EVP_PKEY_derive(pctx, buf, buflen)
+ if code <= 0 then
+ return nil, format_error("pkey:derive: EVP_PKEY_derive", code)
+ end
+
+ return ffi_str(buf, buflen[0])
+end
+
+local function pub_or_priv_is_pri(pub_or_priv)
+ if pub_or_priv == 'private' or pub_or_priv == 'PrivateKey' then
+ return true
+ elseif not pub_or_priv or pub_or_priv == 'public' or pub_or_priv == 'PublicKey' then
+ return false
+ else
+ return nil, string.format("can only export private or public key, not %s", pub_or_priv)
+ end
+end
+
+function _M:tostring(pub_or_priv, fmt, pkcs1)
+ local is_priv, err = pub_or_priv_is_pri(pub_or_priv)
+ if err then
+ return nil, "pkey:tostring: " .. err
+ end
+ return __tostring(self, is_priv, fmt, pkcs1)
+end
+
+function _M:to_PEM(pub_or_priv, pkcs1)
+ return self:tostring(pub_or_priv, "PEM", pkcs1)
+end
+
+function _M.paramgen(config)
+ local typ = config.type
+ local key_type, write_func, get_ctx_func
+ if typ == "EC" then
+ key_type = evp_macro.EVP_PKEY_EC
+ if key_type == 0 then
+ return nil, "pkey.paramgen: the linked OpenSSL library doesn't support EC key"
+ end
+ write_func = C.PEM_write_bio_ECPKParameters
+ get_ctx_func = function(ctx)
+ local ctx = get_pkey_key[key_type](ctx)
+ if ctx == nil then
+ error(format_error("pkey.paramgen: EVP_PKEY_get0_{key}"))
+ end
+ return C.EC_KEY_get0_group(ctx)
+ end
+ elseif typ == "DH" then
+ key_type = evp_macro.EVP_PKEY_DH
+ if key_type == 0 then
+ return nil, "pkey.paramgen: the linked OpenSSL library doesn't support DH key"
+ end
+ write_func = C.PEM_write_bio_DHparams
+ get_ctx_func = get_pkey_key[key_type]
+ else
+ return nil, "pkey.paramgen: unsupported type " .. type
+ end
+
+ local params, err = generate_param(key_type, config)
+ if err then
+ return nil, "pkey.paramgen: generate_param: " .. err
+ end
+
+ local ctx = get_ctx_func(params)
+ if ctx == nil then
+ return nil, format_error("pkey.paramgen: EVP_PKEY_get0_{key}")
+ end
+
+ return bio_util.read_wrap(write_func, ctx)
+end
+
+return _M
diff --git a/server/resty/openssl/provider.lua b/server/resty/openssl/provider.lua
new file mode 100644
index 0000000..2879ac3
--- /dev/null
+++ b/server/resty/openssl/provider.lua
@@ -0,0 +1,136 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+require "resty.openssl.include.provider"
+local param_lib = require "resty.openssl.param"
+local ctx_lib = require "resty.openssl.ctx"
+local null = require("resty.openssl.auxiliary.ctypes").null
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local format_error = require("resty.openssl.err").format_error
+
+if not OPENSSL_3X then
+ error("provider is only supported since OpenSSL 3.0")
+end
+
+local _M = {}
+local mt = {__index = _M}
+
+local ossl_provider_ctx_ct = ffi.typeof('OSSL_PROVIDER*')
+
+function _M.load(name, try)
+ local ctx
+ local libctx = ctx_lib.get_libctx()
+ if try then
+ ctx = C.OSSL_PROVIDER_try_load(libctx, name)
+ if ctx == nil then
+ return nil, format_error("provider.try_load")
+ end
+ else
+ ctx = C.OSSL_PROVIDER_load(libctx, name)
+ if ctx == nil then
+ return nil, format_error("provider.load")
+ end
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ param_types = nil,
+ }, mt), nil
+end
+
+function _M.set_default_search_path(path)
+ C.OSSL_PROVIDER_set_default_search_path(ctx_lib.get_libctx(), path)
+end
+
+function _M.is_available(name)
+ return C.OSSL_PROVIDER_available(ctx_lib.get_libctx(), name) == 1
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(ossl_provider_ctx_ct, l.ctx)
+end
+
+function _M:unload()
+ if C.OSSL_PROVIDER_unload(self.ctx) == nil then
+ return false, format_error("provider:unload")
+ end
+ return true
+end
+
+function _M:self_test()
+ if C.OSSL_PROVIDER_self_test(self.ctx) == nil then
+ return false, format_error("provider:self_test")
+ end
+ return true
+end
+
+local params_well_known = {
+ -- Well known parameter names that core passes to providers
+ ["openssl-version"] = param_lib.OSSL_PARAM_UTF8_PTR,
+ ["provider-name"] = param_lib.OSSL_PARAM_UTF8_PTR,
+ ["module-filename"] = param_lib.OSSL_PARAM_UTF8_PTR,
+
+ -- Well known parameter names that Providers can define
+ ["name"] = param_lib.OSSL_PARAM_UTF8_PTR,
+ ["version"] = param_lib.OSSL_PARAM_UTF8_PTR,
+ ["buildinfo"] = param_lib.OSSL_PARAM_UTF8_PTR,
+ ["status"] = param_lib.OSSL_PARAM_INTEGER,
+ ["security-checks"] = param_lib.OSSL_PARAM_INTEGER,
+}
+
+local function load_gettable_names(ctx)
+ local schema = {}
+ for k, v in pairs(params_well_known) do
+ schema[k] = v
+ end
+
+ local err
+ schema, err = param_lib.parse_params_schema(
+ C.OSSL_PROVIDER_gettable_params(ctx), schema)
+ if err then
+ return nil, err
+ end
+
+ return schema
+end
+
+function _M:get_params(...)
+ local keys = {...}
+ local key_length = #keys
+ if key_length == 0 then
+ return nil, "provider:get_params: at least one key is required"
+ end
+
+ if not self.param_types then
+ local param_types, err = load_gettable_names(self.ctx)
+ if err then
+ return nil, "provider:get_params: " .. err
+ end
+ self.param_types = param_types
+ end
+
+ local buffers = {}
+ for _, key in ipairs(keys) do
+ buffers[key] = null
+ end
+ local req, err = param_lib.construct(buffers, key_length, self.param_types)
+ if not req then
+ return nil, "provider:get_params: failed to construct params: " .. err
+ end
+
+ if C.OSSL_PROVIDER_get_params(self.ctx, req) ~= 1 then
+ return nil, format_error("provider:get_params")
+ end
+
+ buffers, err = param_lib.parse(buffers, key_length, self.param_types)
+ if err then
+ return nil, "provider:get_params: failed to parse params: " .. err
+ end
+
+ if key_length == 1 then
+ return buffers[keys[1]]
+ end
+ return buffers
+end
+
+return _M
diff --git a/server/resty/openssl/rand.lua b/server/resty/openssl/rand.lua
new file mode 100644
index 0000000..be54da9
--- /dev/null
+++ b/server/resty/openssl/rand.lua
@@ -0,0 +1,51 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+
+require "resty.openssl.include.rand"
+local ctx_lib = require "resty.openssl.ctx"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local buf
+local buf_size = 0
+local function bytes(length, private, strength)
+ if type(length) ~= "number" then
+ return nil, "rand.bytes: expect a number at #1"
+ elseif strength and type(strength) ~= "number" then
+ return nil, "rand.bytes: expect a number at #3"
+ end
+ -- generally we don't need manually reseed rng
+ -- https://www.openssl.org/docs/man1.1.1/man3/RAND_seed.html
+
+ -- initialize or resize buffer
+ if not buf or buf_size < length then
+ buf = ctypes.uchar_array(length)
+ buf_size = length
+ end
+
+ local code
+ if OPENSSL_3X then
+ if private then
+ code = C.RAND_priv_bytes_ex(ctx_lib.get_libctx(), buf, length, strength or 0)
+ else
+ code = C.RAND_bytes_ex(ctx_lib.get_libctx(), buf, length, strength or 0)
+ end
+ else
+ if private then
+ code = C.RAND_priv_bytes(buf, length)
+ else
+ code = C.RAND_bytes(buf, length)
+ end
+ end
+ if code ~= 1 then
+ return nil, format_error("rand.bytes", code)
+ end
+
+ return ffi_str(buf, length)
+end
+
+return {
+ bytes = bytes,
+}
diff --git a/server/resty/openssl/rsa.lua b/server/resty/openssl/rsa.lua
new file mode 100644
index 0000000..f3af394
--- /dev/null
+++ b/server/resty/openssl/rsa.lua
@@ -0,0 +1,155 @@
+local ffi = require "ffi"
+local C = ffi.C
+
+local bn_lib = require "resty.openssl.bn"
+
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+_M.params = {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}
+
+local empty_table = {}
+local bn_ptrptr_ct = ffi.typeof("const BIGNUM *[1]")
+function _M.get_parameters(rsa_st)
+ -- {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}
+ return setmetatable(empty_table, {
+ __index = function(_, k)
+ local ptr, ret
+ if OPENSSL_11_OR_LATER then
+ ptr = bn_ptrptr_ct()
+ end
+
+ if k == 'n' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_key(rsa_st, ptr, nil, nil)
+ end
+ elseif k == 'e' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_key(rsa_st, nil, ptr, nil)
+ end
+ elseif k == 'd' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_key(rsa_st, nil, nil, ptr)
+ end
+ elseif k == 'p' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_factors(rsa_st, ptr, nil)
+ end
+ elseif k == 'q' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_factors(rsa_st, nil, ptr)
+ end
+ elseif k == 'dmp1' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_crt_params(rsa_st, ptr, nil, nil)
+ end
+ elseif k == 'dmq1' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_crt_params(rsa_st, nil, ptr, nil)
+ end
+ elseif k == 'iqmp' then
+ if OPENSSL_11_OR_LATER then
+ C.RSA_get0_crt_params(rsa_st, nil, nil, ptr)
+ end
+ else
+ return nil, "rsa.get_parameters: unknown parameter \"" .. k .. "\" for RSA key"
+ end
+
+ if OPENSSL_11_OR_LATER then
+ ret = ptr[0]
+ elseif OPENSSL_10 then
+ ret = rsa_st[k]
+ end
+
+ if ret == nil then
+ return nil
+ end
+ return bn_lib.dup(ret)
+ end
+ }), nil
+end
+
+local function dup_bn_value(v)
+ if not bn_lib.istype(v) then
+ return nil, "expect value to be a bn instance"
+ end
+ local bn = C.BN_dup(v.ctx)
+ if bn == nil then
+ return nil, "BN_dup() failed"
+ end
+ return bn
+end
+
+function _M.set_parameters(rsa_st, opts)
+ local err
+ local opts_bn = {}
+ -- remember which parts of BNs has been added to rsa_st, they should be freed
+ -- by RSA_free and we don't cleanup them on failure
+ local cleanup_from_idx = 1
+ -- dup input
+ local do_set_key, do_set_factors, do_set_crt_params
+ for k, v in pairs(opts) do
+ opts_bn[k], err = dup_bn_value(v)
+ if err then
+ err = "rsa.set_parameters: cannot process parameter \"" .. k .. "\":" .. err
+ goto cleanup_with_error
+ end
+ if k == "n" or k == "e" or k == "d" then
+ do_set_key = true
+ elseif k == "p" or k == "q" then
+ do_set_factors = true
+ elseif k == "dmp1" or k == "dmq1" or k == "iqmp" then
+ do_set_crt_params = true
+ end
+ end
+ if OPENSSL_11_OR_LATER then
+ -- "The values n and e must be non-NULL the first time this function is called on a given RSA object."
+ -- thus we force to set them together
+ local code
+ if do_set_key then
+ code = C.RSA_set0_key(rsa_st, opts_bn["n"], opts_bn["e"], opts_bn["d"])
+ if code == 0 then
+ err = format_error("rsa.set_parameters: RSA_set0_key")
+ goto cleanup_with_error
+ end
+ end
+ cleanup_from_idx = cleanup_from_idx + 3
+ if do_set_factors then
+ code = C.RSA_set0_factors(rsa_st, opts_bn["p"], opts_bn["q"])
+ if code == 0 then
+ err = format_error("rsa.set_parameters: RSA_set0_factors")
+ goto cleanup_with_error
+ end
+ end
+ cleanup_from_idx = cleanup_from_idx + 2
+ if do_set_crt_params then
+ code = C.RSA_set0_crt_params(rsa_st, opts_bn["dmp1"], opts_bn["dmq1"], opts_bn["iqmp"])
+ if code == 0 then
+ err = format_error("rsa.set_parameters: RSA_set0_crt_params")
+ goto cleanup_with_error
+ end
+ end
+ return true
+ elseif OPENSSL_10 then
+ for k, v in pairs(opts_bn) do
+ if rsa_st[k] ~= nil then
+ C.BN_free(rsa_st[k])
+ end
+ rsa_st[k]= v
+ end
+ return true
+ end
+
+::cleanup_with_error::
+ for i, k in pairs(_M.params) do
+ if i >= cleanup_from_idx then
+ C.BN_free(opts_bn[k])
+ end
+ end
+ return false, err
+end
+
+return _M
diff --git a/server/resty/openssl/ssl.lua b/server/resty/openssl/ssl.lua
new file mode 100644
index 0000000..d3eee90
--- /dev/null
+++ b/server/resty/openssl/ssl.lua
@@ -0,0 +1,353 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.ssl"
+
+local nginx_aux = require("resty.openssl.auxiliary.nginx")
+local x509_lib = require("resty.openssl.x509")
+local chain_lib = require("resty.openssl.x509.chain")
+local stack_lib = require("resty.openssl.stack")
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {
+ SSL_VERIFY_NONE = 0x00,
+ SSL_VERIFY_PEER = 0x01,
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02,
+ SSL_VERIFY_CLIENT_ONCE = 0x04,
+ SSL_VERIFY_POST_HANDSHAKE = 0x08,
+}
+
+local ops = {
+ SSL_OP_NO_EXTENDED_MASTER_SECRET = 0x00000001,
+ SSL_OP_CLEANSE_PLAINTEXT = 0x00000002,
+ SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004,
+ SSL_OP_TLSEXT_PADDING = 0x00000010,
+ SSL_OP_SAFARI_ECDHE_ECDSA_BUG = 0x00000040,
+ SSL_OP_IGNORE_UNEXPECTED_EOF = 0x00000080,
+ SSL_OP_DISABLE_TLSEXT_CA_NAMES = 0x00000200,
+ SSL_OP_ALLOW_NO_DHE_KEX = 0x00000400,
+ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800,
+ SSL_OP_NO_QUERY_MTU = 0x00001000,
+ SSL_OP_COOKIE_EXCHANGE = 0x00002000,
+ SSL_OP_NO_TICKET = 0x00004000,
+ SSL_OP_CISCO_ANYCONNECT = 0x00008000,
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000,
+ SSL_OP_NO_COMPRESSION = 0x00020000,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000,
+ SSL_OP_NO_ENCRYPT_THEN_MAC = 0x00080000,
+ SSL_OP_ENABLE_MIDDLEBOX_COMPAT = 0x00100000,
+ SSL_OP_PRIORITIZE_CHACHA = 0x00200000,
+ SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000,
+ SSL_OP_TLS_ROLLBACK_BUG = 0x00800000,
+ SSL_OP_NO_ANTI_REPLAY = 0x01000000,
+ SSL_OP_NO_SSLv3 = 0x02000000,
+ SSL_OP_NO_TLSv1 = 0x04000000,
+ SSL_OP_NO_TLSv1_2 = 0x08000000,
+ SSL_OP_NO_TLSv1_1 = 0x10000000,
+ SSL_OP_NO_TLSv1_3 = 0x20000000,
+ SSL_OP_NO_DTLSv1 = 0x04000000,
+ SSL_OP_NO_DTLSv1_2 = 0x08000000,
+ SSL_OP_NO_RENEGOTIATION = 0x40000000,
+ SSL_OP_CRYPTOPRO_TLSEXT_BUG = 0x80000000,
+}
+ops.SSL_OP_NO_SSL_MASK = ops.SSL_OP_NO_SSLv3 + ops.SSL_OP_NO_TLSv1 + ops.SSL_OP_NO_TLSv1_1
+ + ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
+ops.SSL_OP_NO_DTLS_MASK = ops.SSL_OP_NO_DTLSv1 + ops.SSL_OP_NO_DTLSv1_2
+for k, v in pairs(ops) do
+ _M[k] = v
+end
+
+local mt = {__index = _M}
+
+local ssl_ptr_ct = ffi.typeof('SSL*')
+
+local stack_of_ssl_cipher_iter = function(ctx)
+ return stack_lib.mt_of("SSL_CIPHER", function(x) return x end, {}, true).__ipairs({ctx = ctx})
+end
+
+function _M.from_request()
+ -- don't GC this
+ local ctx, err = nginx_aux.get_req_ssl()
+ if err ~= nil then
+ return nil, err
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ -- the cdata is not manage by Lua, don't GC on Lua side
+ _managed = false,
+ -- this is the client SSL session
+ _server = true,
+ }, mt)
+end
+
+function _M.from_socket(socket)
+ if not socket then
+ return nil, "expect a ngx.socket.tcp instance at #1"
+ end
+ -- don't GC this
+ local ctx, err = nginx_aux.get_socket_ssl(socket)
+ if err ~= nil then
+ return nil, err
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ -- the cdata is not manage by Lua, don't GC on Lua side
+ _managed = false,
+ -- this is the client SSL session
+ _server = false,
+ }, mt)
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(ssl_ptr_ct, l.ctx)
+end
+
+function _M:get_peer_certificate()
+ local x509
+ if OPENSSL_3X then
+ x509 = C.SSL_get1_peer_certificate(self.ctx)
+ else
+ x509 = C.SSL_get_peer_certificate(self.ctx)
+ end
+
+ if x509 == nil then
+ return nil
+ end
+ ffi.gc(x509, C.X509_free)
+
+ local err
+ -- always copy, although the ref counter of returned x509 is
+ -- already increased by one.
+ x509, err = x509_lib.dup(x509)
+ if err then
+ return nil, err
+ end
+
+ return x509
+end
+
+function _M:get_peer_cert_chain()
+ local stack = C.SSL_get_peer_cert_chain(self.ctx)
+
+ if stack == nil then
+ return nil
+ end
+
+ return chain_lib.dup(stack)
+end
+
+-- TLSv1.3
+function _M:set_ciphersuites(ciphers)
+ if C.SSL_set_ciphersuites(self.ctx, ciphers) ~= 1 then
+ return false, format_error("ssl:set_ciphers: SSL_set_ciphersuites")
+ end
+
+ return true
+end
+
+-- TLSv1.2 and lower
+function _M:set_cipher_list(ciphers)
+ if C.SSL_set_cipher_list(self.ctx, ciphers) ~= 1 then
+ return false, format_error("ssl:set_ciphers: SSL_set_cipher_list")
+ end
+
+ return true
+end
+
+function _M:get_ciphers()
+ local ciphers = C.SSL_get_ciphers(self.ctx)
+
+ if ciphers == nil then
+ return nil
+ end
+
+ local ret = {}
+
+ for i, cipher in stack_of_ssl_cipher_iter(ciphers) do
+ cipher = C.SSL_CIPHER_get_name(cipher)
+ if cipher == nil then
+ return nil, format_error("ssl:get_ciphers: SSL_CIPHER_get_name")
+ end
+ ret[i] = ffi_str(cipher)
+ end
+
+ return table.concat(ret, ":")
+end
+
+function _M:get_cipher_name()
+ local cipher = C.SSL_get_current_cipher(self.ctx)
+
+ if cipher == nil then
+ return nil
+ end
+
+ cipher = C.SSL_CIPHER_get_name(cipher)
+ if cipher == nil then
+ return nil, format_error("ssl:get_cipher_name: SSL_CIPHER_get_name")
+ end
+ return ffi_str(cipher)
+end
+
+function _M:set_timeout(tm)
+ local session = C.SSL_get_session(self.ctx)
+
+ if session == nil then
+ return false, format_error("ssl:set_timeout: SSL_get_session")
+ end
+
+ if C.SSL_SESSION_set_timeout(session, tm) ~= 1 then
+ return false, format_error("ssl:set_timeout: SSL_SESSION_set_timeout")
+ end
+ return true
+end
+
+function _M:get_timeout()
+ local session = C.SSL_get_session(self.ctx)
+
+ if session == nil then
+ return false, format_error("ssl:get_timeout: SSL_get_session")
+ end
+
+ return tonumber(C.SSL_SESSION_get_timeout(session))
+end
+
+local ssl_verify_default_cb = ffi_cast("verify_callback", function()
+ return 1
+end)
+
+function _M:set_verify(mode, cb)
+ if self._verify_cb then
+ self._verify_cb:free()
+ end
+
+ if cb then
+ cb = ffi_cast("verify_callback", cb)
+ self._verify_cb = cb
+ end
+
+ C.SSL_set_verify(self.ctx, mode, cb or ssl_verify_default_cb)
+
+ return true
+end
+
+function _M:free_verify_cb()
+ if self._verify_cb then
+ self._verify_cb:free()
+ self._verify_cb = nil
+ end
+end
+
+function _M:add_client_ca(x509)
+ if not self._server then
+ return false, "ssl:add_client_ca is only supported on server side"
+ end
+
+ if not x509_lib.istype(x509) then
+ return false, "expect a x509 instance at #1"
+ end
+
+ if C.SSL_add_client_CA(self.ctx, x509.ctx) ~= 1 then
+ return false, format_error("ssl:add_client_ca: SSL_add_client_CA")
+ end
+
+ return true
+end
+
+function _M:set_options(...)
+ local bitmask = 0
+ for _, opt in ipairs({...}) do
+ bitmask = bit.bor(bitmask, opt)
+ end
+
+ if OPENSSL_10 then
+ bitmask = C.SSL_ctrl(self.ctx, 32, bitmask, nil) -- SSL_CTRL_OPTIONS
+ else
+ bitmask = C.SSL_set_options(self.ctx, bitmask)
+ end
+
+ return tonumber(bitmask)
+end
+
+function _M:get_options(readable)
+ local bitmask
+ if OPENSSL_10 then
+ bitmask = C.SSL_ctrl(self.ctx, 32, 0, nil) -- SSL_CTRL_OPTIONS
+ else
+ bitmask = C.SSL_get_options(self.ctx)
+ end
+
+ if not readable then
+ return tonumber(bitmask)
+ end
+
+ local ret = {}
+ for k, v in pairs(ops) do
+ if bit.band(v, bitmask) > 0 then
+ table.insert(ret, k)
+ end
+ end
+ table.sort(ret)
+
+ return ret
+end
+
+function _M:clear_options(...)
+ local bitmask = 0
+ for _, opt in ipairs({...}) do
+ bitmask = bit.bor(bitmask, opt)
+ end
+
+ if OPENSSL_10 then
+ bitmask = C.SSL_ctrl(self.ctx, 77, bitmask, nil) -- SSL_CTRL_CLEAR_OPTIONS
+ else
+ bitmask = C.SSL_clear_options(self.ctx, bitmask)
+ end
+
+ return tonumber(bitmask)
+end
+
+local valid_protocols = {
+ ["SSLv3"] = ops.SSL_OP_NO_SSLv3,
+ ["TLSv1"] = ops.SSL_OP_NO_TLSv1,
+ ["TLSv1.1"] = ops.SSL_OP_NO_TLSv1_1,
+ ["TLSv1.2"] = ops.SSL_OP_NO_TLSv1_2,
+ ["TLSv1.3"] = ops.SSL_OP_NO_TLSv1_3,
+}
+local any_tlsv1 = ops.SSL_OP_NO_TLSv1_1 + ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
+
+function _M:set_protocols(...)
+ local bitmask = 0
+ for _, prot in ipairs({...}) do
+ local b = valid_protocols[prot]
+ if not b then
+ return nil, "\"" .. prot .. "\" is not a valid protocol"
+ end
+ bitmask = bit.bor(bitmask, b)
+ end
+
+ if bit.band(bitmask, any_tlsv1) > 0 then
+ bitmask = bit.bor(bitmask, ops.SSL_OP_NO_TLSv1)
+ end
+
+ -- first disable all protocols
+ if OPENSSL_10 then
+ C.SSL_ctrl(self.ctx, 32, ops.SSL_OP_NO_SSL_MASK, nil) -- SSL_CTRL_OPTIONS
+ else
+ C.SSL_set_options(self.ctx, ops.SSL_OP_NO_SSL_MASK)
+ end
+
+ -- then enable selected protocols
+ if OPENSSL_10 then
+ return tonumber(C.SSL_clear_options(self.ctx, bitmask))
+ else
+ return tonumber(C.SSL_ctrl(self.ctx, 77, bitmask, nil)) -- SSL_CTRL_CLEAR_OPTIONS)
+ end
+end
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/ssl_ctx.lua b/server/resty/openssl/ssl_ctx.lua
new file mode 100644
index 0000000..dd110f9
--- /dev/null
+++ b/server/resty/openssl/ssl_ctx.lua
@@ -0,0 +1,95 @@
+local ffi = require "ffi"
+local C = ffi.C
+local new_tab = table.new
+local char = string.char
+local concat = table.concat
+
+require "resty.openssl.include.ssl"
+
+local nginx_aux = require("resty.openssl.auxiliary.nginx")
+
+local _M = {}
+local mt = {__index = _M}
+
+local ssl_ctx_ptr_ct = ffi.typeof('SSL_CTX*')
+
+function _M.from_request()
+ -- don't GC this
+ local ctx, err = nginx_aux.get_req_ssl_ctx()
+ if err ~= nil then
+ return nil, err
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ -- the cdata is not manage by Lua, don't GC on Lua side
+ _managed = false,
+ -- this is the Server SSL session
+ _server = true,
+ }, mt)
+end
+
+function _M.from_socket(socket)
+ if not socket then
+ return nil, "expect a ngx.socket.tcp instance at #1"
+ end
+ -- don't GC this
+ local ctx, err = nginx_aux.get_socket_ssl_ctx(socket)
+ if err ~= nil then
+ return nil, err
+ end
+
+ return setmetatable({
+ ctx = ctx,
+ -- the cdata is not manage by Lua, don't GC on Lua side
+ _managed = false,
+ -- this is the client SSL session
+ _server = false,
+ }, mt)
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(ssl_ctx_ptr_ct, l.ctx)
+end
+
+local function encode_alpn_wire(alpns)
+ local ret = new_tab(#alpns*2, 0)
+ for i, alpn in ipairs(alpns) do
+ ret[i*2-1] = char(#alpn)
+ ret[i*2] = alpn
+ end
+
+ return concat(ret, "")
+end
+
+function _M:set_alpns(alpns)
+ if not self._server then
+ return nil, "ssl_ctx:set_alpns is only supported on server side"
+ end
+
+ alpns = encode_alpn_wire(alpns)
+
+ if self._alpn_select_cb then
+ self._alpn_select_cb:free()
+ end
+
+ local alpn_select_cb = ffi.cast("SSL_CTX_alpn_select_cb_func", function(_, out, outlen, client, client_len)
+ local code = ffi.C.SSL_select_next_proto(
+ ffi.cast("unsigned char **", out), outlen,
+ alpns, #alpns,
+ client, client_len)
+ if code ~= 1 then -- OPENSSL_NPN_NEGOTIATED
+ return 3 -- SSL_TLSEXT_ERR_NOACK
+ end
+ return 0 -- SSL_TLSEXT_ERR_OK
+ end)
+
+ C.SSL_CTX_set_alpn_select_cb(self.ctx, alpn_select_cb, nil)
+ -- store the reference to avoid it being GC'ed
+ self._alpn_select_cb = alpn_select_cb
+
+ return true
+end
+
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/stack.lua b/server/resty/openssl/stack.lua
new file mode 100644
index 0000000..9bdc377
--- /dev/null
+++ b/server/resty/openssl/stack.lua
@@ -0,0 +1,159 @@
+
+--[[
+ The OpenSSL stack library. Note `safestack` is not usable here in ffi because
+ those symbols are eaten after preprocessing.
+ Instead, we should do a Lua land type checking by having a nested field indicating
+ which type of cdata its ctx holds.
+]]
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_cast = ffi.cast
+local ffi_gc = ffi.gc
+
+local stack_macro = require "resty.openssl.include.stack"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+local function gc_of(typ)
+ local f = C[typ .. "_free"]
+ return function (st)
+ stack_macro.OPENSSL_sk_pop_free(st, f)
+ end
+end
+
+_M.gc_of = gc_of
+
+_M.mt_of = function(typ, convert, index_tbl, no_gc)
+ if type(typ) ~= "string" then
+ error("expect a string at #1")
+ elseif type(convert) ~= "function" then
+ error("expect a function at #2")
+ end
+
+ local typ_ptr = typ .. "*"
+
+ -- starts from 0
+ local function value_at(ctx, i)
+ local elem = stack_macro.OPENSSL_sk_value(ctx, i)
+ if elem == nil then
+ error(format_error("OPENSSL_sk_value"))
+ end
+ local dup, err = convert(ffi_cast(typ_ptr, elem))
+ if err then
+ error(err)
+ end
+ return dup
+ end
+
+ local function iter(tbl)
+ if not tbl then error("instance is nil") end
+ local i = 0
+ local n = tonumber(stack_macro.OPENSSL_sk_num(tbl.ctx))
+ return function()
+ i = i + 1
+ if i <= n then
+ return i, value_at(tbl.ctx, i-1)
+ end
+ end
+ end
+
+ local ret = {
+ __pairs = iter,
+ __ipairs = iter,
+ __len = function(tbl)
+ if not tbl then error("instance is nil") end
+ return tonumber(stack_macro.OPENSSL_sk_num(tbl.ctx))
+ end,
+ __index = function(tbl, k)
+ if not tbl then error("instance is nil") end
+ local i = tonumber(k)
+ if not i then
+ return index_tbl[k]
+ end
+ local n = stack_macro.OPENSSL_sk_num(tbl.ctx)
+ if i <= 0 or i > n then
+ return nil
+ end
+ return value_at(tbl.ctx, i-1)
+ end,
+ }
+
+ if not no_gc then
+ ret.__gc = gc_of(typ)
+ end
+ return ret
+end
+
+_M.new_of = function(typ)
+ local gc = gc_of(typ)
+ return function()
+ local raw = stack_macro.OPENSSL_sk_new_null()
+ if raw == nil then
+ return nil, "stack.new_of: OPENSSL_sk_new_null() failed"
+ end
+ ffi_gc(raw, gc)
+ return raw
+ end
+end
+
+_M.add_of = function(typ)
+ local ptr = ffi.typeof(typ .. "*")
+ return function(stack, ctx)
+ if not stack then error("instance is nil") end
+ if ctx == nil or not ffi.istype(ptr, ctx) then
+ return false, "stack.add_of: expect a " .. typ .. "* at #1"
+ end
+ local code = stack_macro.OPENSSL_sk_push(stack, ctx)
+ if code == 0 then
+ return false, "stack.add_of: OPENSSL_sk_push() failed"
+ end
+ return true
+ end
+end
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+_M.dup_of = function(_)
+ return function(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "stack.dup_of: expect a stack ctx at #1"
+ end
+ local ctx = stack_macro.OPENSSL_sk_dup(ctx)
+ if ctx == nil then
+ return nil, "stack.dup_of: OPENSSL_sk_dup() failed"
+ end
+ -- if the stack is duplicated: since we don't copy the elements
+ -- then we only control gc of the stack itself here
+ ffi_gc(ctx, stack_macro.OPENSSL_sk_free)
+ return ctx
+ end
+end
+
+-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
+_M.all_func = function(mt)
+ return function(stack)
+ if not stack then error("stack is nil") end
+ local ret = {}
+ local _next = mt.__pairs(stack)
+ while true do
+ local i, elem = _next()
+ if elem then
+ ret[i] = elem
+ else
+ break
+ end
+ end
+ return ret
+ end
+end
+
+_M.deep_copy_of = function(typ)
+ local dup = C[typ .. "_dup"]
+ local free = C[typ .. "_free"]
+
+ return function(ctx)
+ return stack_macro.OPENSSL_sk_deep_copy(ctx, dup, free)
+ end
+end
+
+return _M \ No newline at end of file
diff --git a/server/resty/openssl/version.lua b/server/resty/openssl/version.lua
new file mode 100644
index 0000000..f982b61
--- /dev/null
+++ b/server/resty/openssl/version.lua
@@ -0,0 +1,117 @@
+-- https://github.com/GUI/lua-openssl-ffi/blob/master/lib/openssl-ffi/version.lua
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_str = ffi.string
+
+ffi.cdef[[
+ // 1.0
+ unsigned long SSLeay(void);
+ const char *SSLeay_version(int t);
+ // >= 1.1
+ unsigned long OpenSSL_version_num();
+ const char *OpenSSL_version(int t);
+ // >= 3.0
+ const char *OPENSSL_info(int t);
+ // BoringSSL
+ int BORINGSSL_self_test(void);
+]]
+
+local version_func, info_func
+local types_table
+
+-- >= 1.1
+local ok, version_num = pcall(function()
+ local num = C.OpenSSL_version_num()
+ version_func = C.OpenSSL_version
+ types_table = {
+ VERSION = 0,
+ CFLAGS = 1,
+ BUILT_ON = 2,
+ PLATFORM = 3,
+ DIR = 4,
+ ENGINES_DIR = 5,
+ VERSION_STRING = 6,
+ FULL_VERSION_STRING = 7,
+ MODULES_DIR = 8,
+ CPU_INFO = 9,
+ }
+ return num
+end)
+
+
+if not ok then
+ -- 1.0.x
+ ok, version_num = pcall(function()
+ local num = C.SSLeay()
+ version_func = C.SSLeay_version
+ types_table = {
+ VERSION = 0,
+ CFLAGS = 2,
+ BUILT_ON = 3,
+ PLATFORM = 4,
+ DIR = 5,
+ }
+ return num
+ end)
+end
+
+
+if not ok then
+ error(string.format("OpenSSL has encountered an error: %s; is OpenSSL library loaded?",
+ tostring(version_num)))
+elseif type(version_num) == 'number' and version_num < 0x10000000 then
+ error(string.format("OpenSSL version %s is not supported", tostring(version_num or 0)))
+elseif not version_num then
+ error("Can not get OpenSSL version")
+end
+
+if version_num >= 0x30000000 then
+ local info_table = {
+ INFO_CONFIG_DIR = 1001,
+ INFO_ENGINES_DIR = 1002,
+ INFO_MODULES_DIR = 1003,
+ INFO_DSO_EXTENSION = 1004,
+ INFO_DIR_FILENAME_SEPARATOR = 1005,
+ INFO_LIST_SEPARATOR = 1006,
+ INFO_SEED_SOURCE = 1007,
+ INFO_CPU_SETTINGS = 1008,
+ }
+
+ for k, v in pairs(info_table) do
+ types_table[k] = v
+ end
+
+ info_func = C.OPENSSL_info
+else
+ info_func = function(_)
+ error(string.format("OPENSSL_info is not supported on %s", ffi_str(version_func(0))))
+ end
+end
+
+local BORINGSSL = false
+pcall(function()
+ local _ = C.BORINGSSL_self_test
+ BORINGSSL = true
+end)
+
+return setmetatable({
+ version_num = tonumber(version_num),
+ version_text = ffi_str(version_func(0)),
+ version = function(t)
+ return ffi_str(version_func(t))
+ end,
+ info = function(t)
+ return ffi_str(info_func(t))
+ end,
+ OPENSSL_3X = version_num >= 0x30000000 and version_num < 0x30200000,
+ OPENSSL_30 = version_num >= 0x30000000 and version_num < 0x30100000, -- for backward compat, deprecated
+ OPENSSL_11 = version_num >= 0x10100000 and version_num < 0x10200000,
+ OPENSSL_111 = version_num >= 0x10101000 and version_num < 0x10200000,
+ OPENSSL_11_OR_LATER = version_num >= 0x10100000 and version_num < 0x30200000,
+ OPENSSL_111_OR_LATER = version_num >= 0x10101000 and version_num < 0x30200000,
+ OPENSSL_10 = version_num < 0x10100000 and version_num > 0x10000000,
+ BORINGSSL = BORINGSSL,
+ BORINGSSL_110 = BORINGSSL and version_num >= 0x10100000 and version_num < 0x10101000
+ }, {
+ __index = types_table,
+}) \ No newline at end of file
diff --git a/server/resty/openssl/x509/altname.lua b/server/resty/openssl/x509/altname.lua
new file mode 100644
index 0000000..34bf9e0
--- /dev/null
+++ b/server/resty/openssl/x509/altname.lua
@@ -0,0 +1,248 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+local asn1_macro = require "resty.openssl.include.asn1"
+local stack_lib = require "resty.openssl.stack"
+local name_lib = require "resty.openssl.x509.name"
+local altname_macro = require "resty.openssl.include.x509.altname"
+
+local _M = {}
+
+local general_names_ptr_ct = ffi.typeof("GENERAL_NAMES*")
+
+local STACK = "GENERAL_NAME"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+local types = altname_macro.types
+
+local AF_INET = 2
+local AF_INET6 = 10
+if ffi.os == "OSX" then
+ AF_INET6 = 30
+elseif ffi.os == "BSD" then
+ AF_INET6 = 28
+elseif ffi.os == "Windows" then
+ AF_INET6 = 23
+end
+
+ffi.cdef [[
+ typedef int socklen_t;
+ int inet_pton(int af, const char *restrict src, void *restrict dst);
+ const char *inet_ntop(int af, const void *restrict src,
+ char *restrict dst, socklen_t size);
+]]
+
+local ip_buffer = ffi.new("unsigned char [46]") -- 46 bytes enough for both string ipv6 and binary ipv6
+
+-- similar to GENERAL_NAME_print, but returns value instead of print
+local gn_decode = function(ctx)
+ local typ = ctx.type
+ local k = altname_macro.literals[typ]
+ local v
+ if typ == types.OtherName then
+ v = "OtherName:<unsupported>"
+ elseif typ == types.RFC822Name then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.rfc822Name))
+ elseif typ == types.DNS then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.dNSName))
+ elseif typ == types.X400 then
+ v = "X400:<unsupported>"
+ elseif typ == types.DirName then
+ v = name_lib.dup(ctx.d.directoryName)
+ elseif typ == types.EdiParty then
+ v = "EdiParty:<unsupported>"
+ elseif typ == types.URI then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier))
+ elseif typ == types.IP then
+ v = asn1_macro.ASN1_STRING_get0_data(ctx.d.iPAddress)
+ local l = tonumber(C.ASN1_STRING_length(ctx.d.iPAddress))
+ if l ~= 4 and l ~= 16 then
+ error("Unknown IP address type")
+ end
+ v = C.inet_ntop(l == 4 and AF_INET or AF_INET6, v, ip_buffer, 46)
+ v = ffi_str(v)
+ elseif typ == types.RID then
+ v = "RID:<unsupported>"
+ else
+ error("unknown type" .. typ .. "-> " .. types.OtherName)
+ end
+ return { k, v }
+end
+
+-- shared with info_access
+_M.gn_decode = gn_decode
+
+local mt = stack_lib.mt_of(STACK, gn_decode, _M)
+local mt__pairs = mt.__pairs
+mt.__pairs = function(tbl)
+ local f = mt__pairs(tbl)
+ return function()
+ local _, e = f()
+ if not e then return end
+ return unpack(e)
+ end
+end
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "x509.altname.new: OPENSSL_sk_new_null() failed"
+ end
+ local cast = ffi_cast("GENERAL_NAMES*", ctx)
+
+ local self = setmetatable({
+ ctx = ctx,
+ cast = cast,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(general_names_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(general_names_ptr_ct, ctx) then
+ return nil, "x509.altname.dup: expect a GENERAL_NAMES* ctx at #1"
+ end
+
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ cast = ffi_cast("GENERAL_NAMES*", dup_ctx),
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+local function gn_set(gn, typ, value)
+ if type(typ) ~= 'string' then
+ return "x509.altname:gn_set: expect a string at #1"
+ end
+ local typ_lower = typ:lower()
+ if type(value) ~= 'string' then
+ return "x509.altname:gn_set: except a string at #2"
+ end
+
+ local txt = value
+ local gn_type = types[typ_lower]
+
+ if not gn_type then
+ return "x509.altname:gn_set: unknown type " .. typ
+ end
+
+ if gn_type == types.IP then
+ if C.inet_pton(AF_INET, txt, ip_buffer) == 1 then
+ txt = ffi_str(ip_buffer, 4)
+ elseif C.inet_pton(AF_INET6, txt, ip_buffer) == 1 then
+ txt = ffi_str(ip_buffer, 16)
+ else
+ return "x509.altname:gn_set: invalid IP address " .. txt
+ end
+
+ elseif gn_type ~= types.Email and
+ gn_type ~= types.URI and
+ gn_type ~= types.DNS then
+ return "x509.altname:gn_set: setting type " .. typ .. " is currently not supported"
+ end
+
+ gn.type = gn_type
+
+ local asn1_string = C.ASN1_IA5STRING_new()
+ if asn1_string == nil then
+ return "x509.altname:gn_set: ASN1_STRING_type_new() failed"
+ end
+
+ local code = C.ASN1_STRING_set(asn1_string, txt, #txt)
+ if code ~= 1 then
+ C.ASN1_STRING_free(asn1_string)
+ return "x509.altname:gn_set: ASN1_STRING_set() failed: " .. code
+ end
+ gn.d.ia5 = asn1_string
+end
+
+-- shared with info_access
+_M.gn_set = gn_set
+
+function _M:add(typ, value)
+
+ -- the stack element stays with stack
+ -- we shouldn't add gc handler if it's already been
+ -- pushed to stack. instead, rely on the gc handler
+ -- of the stack to release all memories
+ local gn = C.GENERAL_NAME_new()
+ if gn == nil then
+ return nil, "x509.altname:add: GENERAL_NAME_new() failed"
+ end
+
+ local err = gn_set(gn, typ, value)
+ if err then
+ C.GENERAL_NAME_free(gn)
+ return nil, err
+ end
+
+ local _, err = add(self.ctx, gn)
+ if err then
+ C.GENERAL_NAME_free(gn)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(gn, C.GENERAL_NAME_free)
+ self._elem_refs[self._elem_refs_idx] = gn
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+ return self
+end
+
+_M.all = function(self)
+ local ret = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ ret[k] = v
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__pairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+mt.__tostring = function(self)
+ local values = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ table.insert(values, k .. "=" .. v)
+ else
+ break
+ end
+ end
+ table.sort(values)
+ return table.concat(values, "/")
+end
+
+_M.tostring = mt.__tostring
+
+return _M
diff --git a/server/resty/openssl/x509/chain.lua b/server/resty/openssl/x509/chain.lua
new file mode 100644
index 0000000..5557ea0
--- /dev/null
+++ b/server/resty/openssl/x509/chain.lua
@@ -0,0 +1,76 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+local stack_lib = require "resty.openssl.stack"
+local x509_lib = require "resty.openssl.x509"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "X509"
+local gc = stack_lib.gc_of(STACK)
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local mt = stack_lib.mt_of(STACK, x509_lib.dup, _M)
+
+function _M.new()
+ local raw = new()
+
+ local self = setmetatable({
+ stack_of = STACK,
+ ctx = raw,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
+ and l.stack_of and l.stack_of == STACK
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "x509.chain.dup: expect a stack ctx at #1, got " .. type(ctx)
+ end
+ -- sk_X509_dup plus up ref for each X509 element
+ local ctx = C.X509_chain_up_ref(ctx)
+ if ctx == nil then
+ return nil, "x509.chain.dup: X509_chain_up_ref() failed"
+ end
+ ffi_gc(ctx, gc)
+
+ return setmetatable({
+ stack_of = STACK,
+ ctx = ctx,
+ }, mt)
+end
+
+function _M:add(x509)
+ if not x509_lib.istype(x509) then
+ return nil, "x509.chain:add: expect a x509 instance at #1"
+ end
+
+ local dup = C.X509_dup(x509.ctx)
+ if dup == nil then
+ return nil, format_error("x509.chain:add: X509_dup")
+ end
+
+ local _, err = add(self.ctx, dup)
+ if err then
+ C.X509_free(dup)
+ return nil, err
+ end
+
+ return true
+end
+
+_M.all = stack_lib.all_func(mt)
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/crl.lua b/server/resty/openssl/x509/crl.lua
new file mode 100644
index 0000000..3ee4501
--- /dev/null
+++ b/server/resty/openssl/x509/crl.lua
@@ -0,0 +1,607 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.x509.crl"
+require "resty.openssl.include.pem"
+require "resty.openssl.include.x509v3"
+local asn1_lib = require("resty.openssl.asn1")
+local bn_lib = require("resty.openssl.bn")
+local revoked_lib = require("resty.openssl.x509.revoked")
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local pkey_lib = require("resty.openssl.pkey")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local ctx_lib = require "resty.openssl.ctx"
+local stack_lib = require "resty.openssl.stack"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+local accessors = {}
+
+accessors.set_issuer_name = C.X509_CRL_set_issuer_name
+accessors.set_version = C.X509_CRL_set_version
+
+
+if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
+ accessors.get_last_update = C.X509_CRL_get0_lastUpdate
+ accessors.set_last_update = C.X509_CRL_set1_lastUpdate
+ accessors.get_next_update = C.X509_CRL_get0_nextUpdate
+ accessors.set_next_update = C.X509_CRL_set1_nextUpdate
+ accessors.get_version = C.X509_CRL_get_version
+ accessors.get_issuer_name = C.X509_CRL_get_issuer -- returns internal ptr
+ accessors.get_signature_nid = C.X509_CRL_get_signature_nid
+ -- BORINGSSL_110 exports X509_CRL_get_signature_nid, but just ignored for simplicity
+ accessors.get_revoked = C.X509_CRL_get_REVOKED
+elseif OPENSSL_10 or BORINGSSL_110 then
+ accessors.get_last_update = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.lastUpdate
+ end
+ accessors.set_last_update = C.X509_CRL_set_lastUpdate
+ accessors.get_next_update = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.nextUpdate
+ end
+ accessors.set_next_update = C.X509_CRL_set_nextUpdate
+ accessors.get_version = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(crl.crl.version)
+ end
+ accessors.get_issuer_name = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.issuer
+ end
+ accessors.get_signature_nid = function(crl)
+ if crl == nil or crl.crl == nil or crl.crl.sig_alg == nil then
+ return nil
+ end
+ return C.OBJ_obj2nid(crl.crl.sig_alg.algorithm)
+ end
+ accessors.get_revoked = function(crl)
+ return crl.crl.revoked
+ end
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509_CRL, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_CRL_bio, self.ctx)
+ else
+ return nil, "x509.crl:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+local x509_crl_ptr_ct = ffi.typeof("X509_CRL*")
+
+function _M.new(crl, fmt, properties)
+ local ctx
+ if not crl then
+ if OPENSSL_3X then
+ ctx = C.X509_CRL_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_CRL_new()
+ end
+ if ctx == nil then
+ return nil, "x509.crl.new: X509_CRL_new() failed"
+ end
+ elseif type(crl) == "string" then
+ -- routine for load an existing csr
+ local bio = C.BIO_new_mem_buf(crl, #crl)
+ if bio == nil then
+ return nil, format_error("x509.crl.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509_CRL(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "x509.crl.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_CRL_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.crl.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ else
+ return nil, "x509.crl.new: expect nil or a string at #1"
+ end
+ ffi_gc(ctx, C.X509_CRL_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l and l.ctx and ffi.istype(x509_crl_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_crl_ptr_ct, ctx) then
+ return nil, "x509.crl.dup: expect a x509.crl ctx at #1"
+ end
+ local ctx = C.X509_CRL_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.crl.dup: X509_CRL_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_CRL_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:text()
+ return bio_util.read_wrap(C.X509_CRL_print, self.ctx)
+end
+
+local function revoked_decode(ctx)
+ if OPENSSL_10 then
+ error("x509.crl:revoked_decode: not supported on OpenSSL 1.0")
+ end
+
+ local ret = {}
+ local serial = C.X509_REVOKED_get0_serialNumber(ctx)
+ if serial ~= nil then
+ serial = C.ASN1_INTEGER_to_BN(serial, nil)
+ if serial == nil then
+ error("x509.crl:revoked_decode: ASN1_INTEGER_to_BN() failed")
+ end
+ ffi_gc(serial, C.BN_free)
+ ret["serial_number"] = bn_lib.to_hex({ctx = serial})
+ end
+
+ local date = C.X509_REVOKED_get0_revocationDate(ctx)
+ if date ~= nil then
+ date = asn1_lib.asn1_to_unix(date)
+ ret["revocation_date"] = date
+ end
+
+ return ret
+end
+
+local revoked_mt = stack_lib.mt_of("X509_REVOKED", revoked_decode, _M)
+
+local function nil_iter() return nil end
+local function revoked_iter(self)
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return nil_iter
+ end
+
+ return revoked_mt.__ipairs({ctx = stack})
+end
+
+mt.__pairs = revoked_iter
+mt.__ipairs = revoked_iter
+mt.__index = function(self, k)
+ local i = tonumber(k)
+ if not i then
+ return _M[k]
+ end
+
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return nil
+ end
+
+ return revoked_mt.__index({ctx = stack}, i)
+end
+mt.__len = function(self)
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return 0
+ end
+
+ return revoked_mt.__len({ctx = stack})
+end
+
+_M.all = function(self)
+ local ret = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ ret[k] = v
+ else
+ break
+ end
+ end
+ return ret
+end
+_M.each = mt.__pairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+--- Adds revoked item to stack of revoked certificates of crl
+-- @tparam table Instance of crl module
+-- @tparam table Instance of revoked module
+-- @treturn boolean true if revoked item was successfully added or false otherwise
+-- @treturn[opt] string Returns optional error message in case of error
+function _M:add_revoked(revoked)
+ if not revoked_lib.istype(revoked) then
+ return false, "x509.crl:add_revoked: expect a revoked instance at #1"
+ end
+ local ctx = C.X509_REVOKED_dup(revoked.ctx)
+ if ctx == nil then
+ return nil, "x509.crl:add_revoked: X509_REVOKED_dup() failed"
+ end
+
+ if C.X509_CRL_add0_revoked(self.ctx, ctx) == 0 then
+ return false, format_error("x509.crl:add_revoked")
+ end
+
+ return true
+end
+
+local ptr_ptr_of_x509_revoked = ffi.typeof("X509_REVOKED*[1]")
+function _M:get_by_serial(sn)
+ local bn, err
+ if bn_lib.istype(sn) then
+ bn = sn
+ elseif type(sn) == "string" then
+ bn, err = bn_lib.from_hex(sn)
+ if err then
+ return nil, "x509.crl:find: can't decode bn: " .. err
+ end
+ else
+ return nil, "x509.crl:find: expect a bn instance at #1"
+ end
+
+ local sn_asn1 = C.BN_to_ASN1_INTEGER(bn.ctx, nil)
+ if sn_asn1 == nil then
+ return nil, "x509.crl:find: BN_to_ASN1_INTEGER() failed"
+ end
+ ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
+
+ local pp = ptr_ptr_of_x509_revoked()
+ local code = C.X509_CRL_get0_by_serial(self.ctx, pp, sn_asn1)
+ if code == 1 then
+ return revoked_decode(pp[0])
+ elseif code == 2 then
+ return nil, "not revoked (removeFromCRL)"
+ end
+
+ -- 0 or other
+ return nil
+end
+
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.crl:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509.crl:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509.crl:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_CRL_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509.crl:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.crl:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_CRL_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509.crl:verify", code)
+ end
+end
+
+-- AUTO GENERATED
+local function get_extension(ctx, nid_txt, last_pos)
+ last_pos = (last_pos or 0) - 1
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+ local pos = C.X509_CRL_get_ext_by_NID(ctx, nid, last_pos)
+ if pos == -1 then
+ return nil
+ end
+ local ctx = C.X509_CRL_get_ext(ctx, pos)
+ if ctx == nil then
+ return nil, nil, format_error()
+ end
+ return ctx, pos
+end
+
+-- AUTO GENERATED
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509.crl:add_extension: expect a x509.extension instance at #1"
+ end
+
+ -- X509_CRL_add_ext returnes the stack on success, and NULL on error
+ -- the X509_EXTENSION ctx is dupped internally
+ if C.X509_CRL_add_ext(self.ctx, extension.ctx, -1) == nil then
+ return false, format_error("x509.crl:add_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509.crl:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509.crl:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local X509_CRL_delete_ext
+if OPENSSL_11_OR_LATER then
+ X509_CRL_delete_ext = C.X509_CRL_delete_ext
+elseif OPENSSL_10 then
+ X509_CRL_delete_ext = function(ctx, pos)
+ return C.X509v3_delete_ext(ctx.crl.extensions, pos)
+ end
+else
+ X509_CRL_delete_ext = function(...)
+ error("X509_CRL_delete_ext undefined")
+ end
+end
+
+-- AUTO GENERATED
+function _M:set_extension(extension, last_pos)
+ if not extension_lib.istype(extension) then
+ return false, "x509.crl:set_extension: expect a x509.extension instance at #1"
+ end
+
+ last_pos = (last_pos or 0) - 1
+
+ local nid = extension:get_object().nid
+ local pos = C.X509_CRL_get_ext_by_NID(self.ctx, nid, last_pos)
+ -- pos may be -1, which means not found, it's fine, we will add new one instead of replace
+
+ local removed = X509_CRL_delete_ext(self.ctx, pos)
+ C.X509_EXTENSION_free(removed)
+
+ if C.X509_CRL_add_ext(self.ctx, extension.ctx, pos) == nil then
+ return false, format_error("x509.crl:set_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.crl:set_extension_critical: " .. err
+ end
+
+ if C.X509_EXTENSION_set_critical(ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509.crl:set_extension_critical")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.crl:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- AUTO GENERATED
+function _M:get_issuer_name()
+ local got = accessors.get_issuer_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_issuer_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.crl:set_issuer_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_issuer_name(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_issuer_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_last_update()
+ local got = accessors.get_last_update(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_last_update(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_last_update: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_last_update(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_last_update")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_next_update()
+ local got = accessors.get_next_update(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_next_update(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_next_update: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_next_update(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_next_update")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_version")
+ end
+ return true
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
+
diff --git a/server/resty/openssl/x509/csr.lua b/server/resty/openssl/x509/csr.lua
new file mode 100644
index 0000000..08c4860
--- /dev/null
+++ b/server/resty/openssl/x509/csr.lua
@@ -0,0 +1,531 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.pem"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.x509.csr"
+require "resty.openssl.include.asn1"
+local stack_macro = require "resty.openssl.include.stack"
+local stack_lib = require "resty.openssl.stack"
+local pkey_lib = require "resty.openssl.pkey"
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local extensions_lib = require("resty.openssl.x509.extensions")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+local accessors = {}
+
+accessors.set_subject_name = C.X509_REQ_set_subject_name
+accessors.get_pubkey = C.X509_REQ_get_pubkey
+accessors.set_pubkey = C.X509_REQ_set_pubkey
+accessors.set_version = C.X509_REQ_set_version
+
+if OPENSSL_11_OR_LATER or BORINGSSL_110 then
+ accessors.get_signature_nid = C.X509_REQ_get_signature_nid
+elseif OPENSSL_10 then
+ accessors.get_signature_nid = function(csr)
+ if csr == nil or csr.sig_alg == nil then
+ return nil
+ end
+ return C.OBJ_obj2nid(csr.sig_alg.algorithm)
+ end
+end
+
+if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
+ accessors.get_subject_name = C.X509_REQ_get_subject_name -- returns internal ptr
+ accessors.get_version = C.X509_REQ_get_version
+elseif OPENSSL_10 or BORINGSSL_110 then
+ accessors.get_subject_name = function(csr)
+ if csr == nil or csr.req_info == nil then
+ return nil
+ end
+ return csr.req_info.subject
+ end
+ accessors.get_version = function(csr)
+ if csr == nil or csr.req_info == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(csr.req_info.version)
+ end
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509_REQ, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_REQ_bio, self.ctx)
+ else
+ return nil, "x509.csr:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+local x509_req_ptr_ct = ffi.typeof("X509_REQ*")
+
+local stack_ptr_type = ffi.typeof("struct stack_st *[1]")
+local x509_extensions_gc = stack_lib.gc_of("X509_EXTENSION")
+
+function _M.new(csr, fmt, properties)
+ local ctx
+ if not csr then
+ if OPENSSL_3X then
+ ctx = C.X509_REQ_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_REQ_new()
+ end
+ if ctx == nil then
+ return nil, "x509.csr.new: X509_REQ_new() failed"
+ end
+ elseif type(csr) == "string" then
+ -- routine for load an existing csr
+ local bio = C.BIO_new_mem_buf(csr, #csr)
+ if bio == nil then
+ return nil, format_error("x509.csr.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509_REQ(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "x509.csr.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_REQ_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.csr.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ else
+ return nil, "x509.csr.new: expect nil or a string at #1"
+ end
+ ffi_gc(ctx, C.X509_REQ_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l and l.ctx and ffi.istype(x509_req_ptr_ct, l.ctx)
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:check_private_key(key)
+ if not pkey_lib.istype(key) then
+ return false, "x509.csr:check_private_key: except a pkey instance at #1"
+ end
+
+ if not key:is_private() then
+ return false, "x509.csr:check_private_key: not a private key"
+ end
+
+ if C.X509_REQ_check_private_key(self.ctx, key.ctx) == 1 then
+ return true
+ end
+ return false, format_error("x509.csr:check_private_key")
+end
+
+--- Get all csr extensions
+-- @tparam table self Instance of csr
+-- @treturn Extensions object
+function _M:get_extensions()
+ local extensions = C.X509_REQ_get_extensions(self.ctx)
+ -- GC handler is sk_X509_EXTENSION_pop_free
+ ffi_gc(extensions, x509_extensions_gc)
+
+ return extensions_lib.dup(extensions)
+end
+
+local function get_extension(ctx, nid_txt, last_pos)
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+
+ local extensions = C.X509_REQ_get_extensions(ctx)
+ if extensions == nil then
+ return nil, nil, format_error("csr.get_extension: X509_REQ_get_extensions")
+ end
+ ffi_gc(extensions, x509_extensions_gc)
+
+ -- make 1-index array to 0-index
+ last_pos = (last_pos or 0) -1
+ local ext_idx = C.X509v3_get_ext_by_NID(extensions, nid, last_pos)
+ if ext_idx == -1 then
+ err = ("X509v3_get_ext_by_NID extension for %d not found"):format(nid)
+ return nil, -1, format_error(err)
+ end
+
+ local ctx = C.X509v3_get_ext(extensions, ext_idx)
+ if ctx == nil then
+ return nil, nil, format_error("X509v3_get_ext")
+ end
+
+ return ctx, ext_idx, nil
+end
+
+--- Get a csr extension
+-- @tparam table self Instance of csr
+-- @tparam string|number Nid number or name of the extension
+-- @tparam number Position to start looking for the extension; default to look from start if omitted
+-- @treturn Parsed extension object or nil if not found
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509.csr:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509.csr:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local function modify_extension(replace, ctx, nid, toset, crit)
+ local extensions_ptr = stack_ptr_type()
+ extensions_ptr[0] = C.X509_REQ_get_extensions(ctx)
+ local need_cleanup = extensions_ptr[0] ~= nil and
+ -- extensions_ptr being nil is fine: it may just because there's no extension yet
+ -- https://github.com/openssl/openssl/commit/2039ac07b401932fa30a05ade80b3626e189d78a
+ -- introduces a change that a empty stack instead of NULL will be returned in no extension
+ -- is found. so we need to double check the number if it's not NULL.
+ stack_macro.OPENSSL_sk_num(extensions_ptr[0]) > 0
+
+ local flag
+ if replace then
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ flag = 0x2
+ else
+ -- x509v3.h: # define X509V3_ADD_APPEND 1L
+ flag = 0x1
+ end
+
+ local code = C.X509V3_add1_i2d(extensions_ptr, nid, toset, crit and 1 or 0, flag)
+ -- when the stack is newly allocated, we want to cleanup the newly created stack as well
+ -- setting the gc handler here as it's mutated in X509V3_add1_i2d if it's pointing to NULL
+ ffi_gc(extensions_ptr[0], x509_extensions_gc)
+ if code ~= 1 then
+ return false, format_error("X509V3_add1_i2d", code)
+ end
+
+ code = C.X509_REQ_add_extensions(ctx, extensions_ptr[0])
+ if code ~= 1 then
+ return false, format_error("X509_REQ_add_extensions", code)
+ end
+
+ if need_cleanup then
+ -- cleanup old attributes
+ -- delete the first only, why?
+ local attr = C.X509_REQ_delete_attr(ctx, 0)
+ if attr ~= nil then
+ C.X509_ATTRIBUTE_free(attr)
+ end
+ end
+
+ -- mark encoded form as invalid so next time it will be re-encoded
+ if OPENSSL_11_OR_LATER then
+ C.i2d_re_X509_REQ_tbs(ctx, nil)
+ else
+ ctx.req_info.enc.modified = 1
+ end
+
+ return true
+end
+
+local function add_extension(...)
+ return modify_extension(false, ...)
+end
+
+local function replace_extension(...)
+ return modify_extension(true, ...)
+end
+
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ local nid = extension:get_object().nid
+ local toset = extension_lib.to_data(extension, nid)
+ return add_extension(self.ctx, nid, toset.ctx, extension:get_critical())
+end
+
+function _M:set_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ local nid = extension:get_object().nid
+ local toset = extension_lib.to_data(extension, nid)
+ return replace_extension(self.ctx, nid, toset.ctx, extension:get_critical())
+end
+
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, "x509.csr:set_extension_critical: " .. err
+ end
+
+ local extension, _, err = get_extension(self.ctx, nid, last_pos)
+ if err then
+ return nil, "x509.csr:set_extension_critical: " .. err
+ end
+
+ local toset = extension_lib.to_data({
+ ctx = extension
+ }, nid)
+ return replace_extension(self.ctx, nid, toset.ctx, crit and 1 or 0)
+end
+
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.csr:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.csr:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509.csr:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509.csr:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_REQ_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509.csr:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.csr:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_REQ_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509.csr:verify", code)
+ end
+end
+-- AUTO GENERATED
+function _M:get_subject_name()
+ local got = accessors.get_subject_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_subject_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_subject_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_subject_name(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_subject_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_pubkey()
+ local got = accessors.get_pubkey(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.pkey")
+ -- returned a copied instance directly
+ return lib.new(got)
+end
+
+-- AUTO GENERATED
+function _M:set_pubkey(toset)
+ local lib = require("resty.openssl.pkey")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_pubkey: expect a pkey instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_pubkey(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_pubkey")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.csr:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_version")
+ end
+ return true
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name()
+ local crit = ctypes.ptr_of_int()
+ local extensions = C.X509_REQ_get_extensions(self.ctx)
+ -- GC handler is sk_X509_EXTENSION_pop_free
+ ffi_gc(extensions, x509_extensions_gc)
+ local got = C.X509V3_get_d2i(extensions, NID_subject_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509.csr:get_subject_alt_name: extension of subject_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509.csr:get_subject_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_subject_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ return replace_extension(self.ctx, NID_subject_alt_name, toset)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_subject_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name_critical()
+ return _M.get_extension_critical(self, NID_subject_alt_name)
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
+
diff --git a/server/resty/openssl/x509/extension.lua b/server/resty/openssl/x509/extension.lua
new file mode 100644
index 0000000..ca23158
--- /dev/null
+++ b/server/resty/openssl/x509/extension.lua
@@ -0,0 +1,281 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_new = ffi.new
+local ffi_cast = ffi.cast
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509.extension"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.bio"
+require "resty.openssl.include.conf"
+local asn1_macro = require("resty.openssl.include.asn1")
+local objects_lib = require "resty.openssl.objects"
+local stack_lib = require("resty.openssl.stack")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local _M = {}
+local mt = { __index = _M }
+
+local x509_extension_ptr_ct = ffi.typeof("X509_EXTENSION*")
+
+local extension_types = {
+ issuer = "resty.openssl.x509",
+ subject = "resty.openssl.x509",
+ request = "resty.openssl.x509.csr",
+ crl = "resty.openssl.x509.crl",
+}
+
+if OPENSSL_3X then
+ extension_types["issuer_pkey"] = "resty.openssl.pkey"
+end
+
+local nconf_load
+if BORINGSSL then
+ nconf_load = function()
+ return nil, "NCONF_load_bio not exported in BoringSSL"
+ end
+else
+ nconf_load = function(conf, str)
+ local bio = C.BIO_new_mem_buf(str, #str)
+ if bio == nil then
+ return format_error("BIO_new_mem_buf")
+ end
+ ffi_gc(bio, C.BIO_free)
+
+ if C.NCONF_load_bio(conf, bio, nil) ~= 1 then
+ return format_error("NCONF_load_bio")
+ end
+ end
+end
+
+function _M.new(txtnid, value, data)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ if type(value) ~= 'string' then
+ return nil, "x509.extension.new: expect string at #2"
+ end
+ -- get a ptr and also zerofill the struct
+ local x509_ctx_ptr = ffi_new('X509V3_CTX[1]')
+
+ local conf = C.NCONF_new(nil)
+ if conf == nil then
+ return nil, format_error("NCONF_new")
+ end
+ ffi_gc(conf, C.NCONF_free)
+
+ if type(data) == 'table' then
+ local args = {}
+ if data.db then
+ if type(data.db) ~= 'string' then
+ return nil, "x509.extension.new: expect data.db must be a string"
+ end
+ err = nconf_load(conf, data)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ end
+
+ for k, t in pairs(extension_types) do
+ if data[k] then
+ local lib = require(t)
+ if not lib.istype(data[k]) then
+ return nil, "x509.extension.new: expect data." .. k .. " to be a " .. t .. " instance"
+ end
+ args[k] = data[k].ctx
+ end
+ end
+ C.X509V3_set_ctx(x509_ctx_ptr[0], args.issuer, args.subject, args.request, args.crl, 0)
+
+ if OPENSSL_3X and args.issuer_pkey then
+ if C.X509V3_set_issuer_pkey(x509_ctx_ptr[0], args.issuer_pkey) ~= 1 then
+ return nil, format_error("x509.extension.new: X509V3_set_issuer_pkey")
+ end
+ end
+
+ elseif type(data) == 'string' then
+ err = nconf_load(conf, data)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ elseif data then
+ return nil, "x509.extension.new: expect nil, string a table at #3"
+ end
+
+ -- setting conf is required for some extensions to load
+ -- crypto/x509/v3_conf.c:do_ext_conf "else if (method->r2i) {" branch
+ C.X509V3_set_nconf(x509_ctx_ptr[0], conf)
+
+ local ctx = C.X509V3_EXT_nconf_nid(conf, x509_ctx_ptr[0], nid, value)
+ if ctx == nil then
+ return nil, format_error("x509.extension.new")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_extension_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_extension_ptr_ct, ctx) then
+ return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
+ end
+ local ctx = C.X509_EXTENSION_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.extension.dup: X509_EXTENSION_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.from_der(value, txtnid, crit)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.from_der: " .. err
+ end
+ if type(value) ~= 'string' then
+ return nil, "x509.extension.from_der: expect string at #1"
+ end
+
+ local asn1 = C.ASN1_STRING_new()
+ if asn1 == nil then
+ return nil, format_error("x509.extension.from_der: ASN1_STRING_new")
+ end
+ ffi_gc(asn1, C.ASN1_STRING_free)
+
+ if C.ASN1_STRING_set(asn1, value, #value) ~= 1 then
+ return nil, format_error("x509.extension.from_der: ASN1_STRING_set")
+ end
+
+ local ctx = C.X509_EXTENSION_create_by_NID(nil, nid, crit and 1 or 0, asn1)
+ if ctx == nil then
+ return nil, format_error("x509.extension.from_der: X509_EXTENSION_create_by_NID")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:to_der()
+ local asn1 = C.X509_EXTENSION_get_data(self.ctx)
+
+ return ffi_str(asn1_macro.ASN1_STRING_get0_data(asn1))
+end
+
+function _M.from_data(any, txtnid, crit)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.from_der: " .. err
+ end
+
+ if type(any) ~= "table" or type(any.ctx) ~= "cdata" then
+ return nil, "x509.extension.from_data: expect a table with ctx at #1"
+ elseif type(nid) ~= "number" then
+ return nil, "x509.extension.from_data: expect a table at #2"
+ end
+
+ local ctx = C.X509V3_EXT_i2d(nid, crit and 1 or 0, any.ctx)
+ if ctx == nil then
+ return nil, format_error("x509.extension.from_data: X509V3_EXT_i2d")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+function _M.to_data(extension, nid)
+ if not _M.istype(extension) then
+ return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
+ elseif type(nid) ~= "number" then
+ return nil, "x509.extension.to_data: expect a table at #2"
+ end
+
+ local void_ptr = C.X509V3_EXT_d2i(extension.ctx)
+ if void_ptr == nil then
+ return nil, format_error("x509.extension:to_data: X509V3_EXT_d2i")
+ end
+
+ if nid == NID_subject_alt_name then
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ ffi_gc(void_ptr, stack_lib.gc_of("GENERAL_NAME"))
+ local got = ffi_cast("GENERAL_NAMES*", void_ptr)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+ end
+
+ return nil, string.format("x509.extension:to_data: don't know how to convert to NID %d", nid)
+end
+
+function _M:get_object()
+ -- retruns the internal pointer
+ local asn1 = C.X509_EXTENSION_get_object(self.ctx)
+
+ return objects_lib.obj2table(asn1)
+end
+
+function _M:get_critical()
+ return C.X509_EXTENSION_get_critical(self.ctx) == 1
+end
+
+function _M:set_critical(crit)
+ if C.X509_EXTENSION_set_critical(self.ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509.extension:set_critical")
+ end
+ return true
+end
+
+function _M:tostring()
+ local ret, err = bio_util.read_wrap(C.X509V3_EXT_print, self.ctx, 0, 0)
+ if not err then
+ return ret
+ end
+ -- fallback to ASN.1 print
+ local asn1 = C.X509_EXTENSION_get_data(self.ctx)
+ return bio_util.read_wrap(C.ASN1_STRING_print, asn1)
+end
+
+_M.text = _M.tostring
+
+mt.__tostring = function(tbl)
+ local txt, err = _M.text(tbl)
+ if err then
+ error(err)
+ end
+ return txt
+end
+
+
+return _M
diff --git a/server/resty/openssl/x509/extension/dist_points.lua b/server/resty/openssl/x509/extension/dist_points.lua
new file mode 100644
index 0000000..b1d419b
--- /dev/null
+++ b/server/resty/openssl/x509/extension/dist_points.lua
@@ -0,0 +1,75 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+local altname_lib = require "resty.openssl.x509.altname"
+local stack_lib = require "resty.openssl.stack"
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "DIST_POINT"
+local new = stack_lib.new_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+-- TODO: return other attributes?
+local cdp_decode_fullname = function(ctx)
+ return altname_lib.dup(ctx.distpoint.name.fullname)
+end
+
+local mt = stack_lib.mt_of(STACK, cdp_decode_fullname, _M)
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "OPENSSL_sk_new_null() failed"
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(stack_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "expect a stack ctx at #1"
+ end
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+_M.all = function(stack)
+ local ret = {}
+ local _next = mt.__ipairs(stack)
+ while true do
+ local i, e = _next()
+ if i then
+ ret[i] = e
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/extension/info_access.lua b/server/resty/openssl/x509/extension/info_access.lua
new file mode 100644
index 0000000..21025a8
--- /dev/null
+++ b/server/resty/openssl/x509/extension/info_access.lua
@@ -0,0 +1,137 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.err"
+local altname_lib = require "resty.openssl.x509.altname"
+local stack_lib = require "resty.openssl.stack"
+
+local _M = {}
+
+local authority_info_access_ptr_ct = ffi.typeof("AUTHORITY_INFO_ACCESS*")
+
+local STACK = "ACCESS_DESCRIPTION"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+local aia_decode = function(ctx)
+ local nid = C.OBJ_obj2nid(ctx.method)
+ local gn = altname_lib.gn_decode(ctx.location)
+ return { nid, unpack(gn) }
+end
+
+local mt = stack_lib.mt_of(STACK, aia_decode, _M)
+local mt__pairs = mt.__pairs
+mt.__pairs = function(tbl)
+ local f = mt__pairs(tbl)
+ return function()
+ local _, e = f()
+ if not e then return end
+ return unpack(e)
+ end
+end
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "OPENSSL_sk_new_null() failed"
+ end
+ local cast = ffi_cast("AUTHORITY_INFO_ACCESS*", ctx)
+
+ local self = setmetatable({
+ ctx = ctx,
+ cast = cast,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(authority_info_access_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(authority_info_access_ptr_ct, ctx) then
+ return nil, "expect a AUTHORITY_INFO_ACCESS* ctx at #1"
+ end
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ cast = ffi_cast("AUTHORITY_INFO_ACCESS*", dup_ctx),
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+function _M:add(nid, typ, value)
+ -- the stack element stays with stack
+ -- we shouldn't add gc handler if it's already been
+ -- pushed to stack. instead, rely on the gc handler
+ -- of the stack to release all memories
+ local ad = C.ACCESS_DESCRIPTION_new()
+ if ad == nil then
+ return nil, "ACCESS_DESCRIPTION_new() failed"
+ end
+
+ -- C.ASN1_OBJECT_free(ad.method)
+
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ C.ACCESS_DESCRIPTION_free(ad)
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, "invalid NID text " .. (nid or "nil")
+ end
+
+ ad.method = asn1
+
+ local err = altname_lib.gn_set(ad.location, typ, value)
+ if err then
+ C.ACCESS_DESCRIPTION_free(ad)
+ return nil, err
+ end
+
+ local _, err = add(self.ctx, ad)
+ if err then
+ C.ACCESS_DESCRIPTION_free(ad)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(ad, C.ACCESS_DESCRIPTION_free)
+ self._elem_refs[self._elem_refs_idx] = ad
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+ return self
+end
+
+_M.all = function(stack)
+ local ret = {}
+ local _next = mt.__ipairs(stack)
+ while true do
+ local i, e = _next()
+ if i then
+ ret[i] = e
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/extensions.lua b/server/resty/openssl/x509/extensions.lua
new file mode 100644
index 0000000..3b64b8a
--- /dev/null
+++ b/server/resty/openssl/x509/extensions.lua
@@ -0,0 +1,84 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+local stack_lib = require "resty.openssl.stack"
+local extension_lib = require "resty.openssl.x509.extension"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "X509_EXTENSION"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+local mt = stack_lib.mt_of(STACK, extension_lib.dup, _M)
+
+function _M.new()
+ local raw = new()
+
+ local self = setmetatable({
+ stack_of = STACK,
+ ctx = raw,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
+ and l.stack_of and l.stack_of == STACK
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "x509.extensions.dup: expect a stack ctx at #1, got " .. type(ctx)
+ end
+
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+function _M:add(extension)
+ if not extension_lib.istype(extension) then
+ return nil, "expect a x509.extension instance at #1"
+ end
+
+ local dup = C.X509_EXTENSION_dup(extension.ctx)
+ if dup == nil then
+ return nil, format_error("extensions:add: X509_EXTENSION_dup")
+ end
+
+ local _, err = add(self.ctx, dup)
+ if err then
+ C.X509_EXTENSION_free(dup)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(dup, C.X509_EXTENSION_free)
+ self._elem_refs[self._elem_refs_idx] = dup
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+
+ return true
+end
+
+_M.all = stack_lib.all_func(mt)
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/init.lua b/server/resty/openssl/x509/init.lua
new file mode 100644
index 0000000..5c259c8
--- /dev/null
+++ b/server/resty/openssl/x509/init.lua
@@ -0,0 +1,1071 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.evp"
+require "resty.openssl.include.objects"
+local stack_macro = require("resty.openssl.include.stack")
+local stack_lib = require("resty.openssl.stack")
+local asn1_lib = require("resty.openssl.asn1")
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local pkey_lib = require("resty.openssl.pkey")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+-- accessors provides an openssl version neutral interface to lua layer
+-- it doesn't handle any error, expect that to be implemented in
+-- _M.set_X or _M.get_X
+local accessors = {}
+
+accessors.get_pubkey = C.X509_get_pubkey -- returns new evp_pkey instance, don't need to dup
+accessors.set_pubkey = C.X509_set_pubkey
+accessors.set_version = C.X509_set_version
+accessors.set_serial_number = C.X509_set_serialNumber
+accessors.get_subject_name = C.X509_get_subject_name -- returns internal ptr, we dup it
+accessors.set_subject_name = C.X509_set_subject_name
+accessors.get_issuer_name = C.X509_get_issuer_name -- returns internal ptr, we dup it
+accessors.set_issuer_name = C.X509_set_issuer_name
+accessors.get_signature_nid = C.X509_get_signature_nid
+
+-- generally, use get1 if we return a lua table wrapped ctx which doesn't support dup.
+-- in that case, a new struct is returned from C api, and we will handle gc.
+-- openssl will increment the reference count for returned ptr, and won't free it when
+-- parent struct is freed.
+-- otherwise, use get0, which returns an internal pointer, we don't need to free it up.
+-- it will be gone together with the parent struct.
+
+if BORINGSSL_110 then
+ accessors.get_not_before = C.X509_get0_notBefore -- returns internal ptr, we convert to number
+ accessors.set_not_before = C.X509_set_notBefore
+ accessors.get_not_after = C.X509_get0_notAfter -- returns internal ptr, we convert to number
+ accessors.set_not_after = C.X509_set_notAfter
+ accessors.get_version = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(x509.cert_info.version)
+ end
+ accessors.get_serial_number = C.X509_get_serialNumber -- returns internal ptr, we convert to bn
+elseif OPENSSL_11_OR_LATER then
+ accessors.get_not_before = C.X509_get0_notBefore -- returns internal ptr, we convert to number
+ accessors.set_not_before = C.X509_set1_notBefore
+ accessors.get_not_after = C.X509_get0_notAfter -- returns internal ptr, we convert to number
+ accessors.set_not_after = C.X509_set1_notAfter
+ accessors.get_version = C.X509_get_version -- returns int
+ accessors.get_serial_number = C.X509_get0_serialNumber -- returns internal ptr, we convert to bn
+elseif OPENSSL_10 then
+ accessors.get_not_before = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return x509.cert_info.validity.notBefore
+ end
+ accessors.set_not_before = C.X509_set_notBefore
+ accessors.get_not_after = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return x509.cert_info.validity.notAfter
+ end
+ accessors.set_not_after = C.X509_set_notAfter
+ accessors.get_version = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(x509.cert_info.version)
+ end
+ accessors.get_serial_number = C.X509_get_serialNumber -- returns internal ptr, we convert to bn
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_bio, self.ctx)
+ else
+ return nil, "x509:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+
+local x509_ptr_ct = ffi.typeof("X509*")
+
+-- only PEM format is supported for now
+function _M.new(cert, fmt, properties)
+ local ctx
+ if not cert then
+ -- routine for create a new cert
+ if OPENSSL_3X then
+ ctx = C.X509_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_new()
+ end
+ if ctx == nil then
+ return nil, format_error("x509.new")
+ end
+ ffi_gc(ctx, C.X509_free)
+
+ C.X509_gmtime_adj(accessors.get_not_before(ctx), 0)
+ C.X509_gmtime_adj(accessors.get_not_after(ctx), 0)
+ elseif type(cert) == "string" then
+ -- routine for load an existing cert
+ local bio = C.BIO_new_mem_buf(cert, #cert)
+ if bio == nil then
+ return nil, format_error("x509.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ C.BIO_free(bio)
+ return nil, "x509.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ ffi_gc(ctx, C.X509_free)
+ elseif type(cert) == 'cdata' then
+ if ffi.istype(x509_ptr_ct, cert) then
+ ctx = cert
+ ffi_gc(ctx, C.X509_free)
+ else
+ return nil, "x509.new: expect a X509* cdata at #1"
+ end
+ else
+ return nil, "x509.new: expect nil or a string at #1"
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_ptr_ct, ctx) then
+ return nil, "x509.dup: expect a x509 ctx at #1"
+ end
+ local ctx = C.X509_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.dup: X509_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:set_lifetime(not_before, not_after)
+ local ok, err
+ if not_before then
+ ok, err = self:set_not_before(not_before)
+ if err then
+ return ok, err
+ end
+ end
+
+ if not_after then
+ ok, err = self:set_not_after(not_after)
+ if err then
+ return ok, err
+ end
+ end
+
+ return true
+end
+
+function _M:get_lifetime()
+ local not_before, err = self:get_not_before()
+ if not_before == nil then
+ return nil, nil, err
+ end
+ local not_after, err = self:get_not_after()
+ if not_after == nil then
+ return nil, nil, err
+ end
+
+ return not_before, not_after, nil
+end
+
+-- note: index is 0 based
+local OPENSSL_STRING_value_at = function(ctx, i)
+ local ct = ffi_cast("OPENSSL_STRING", stack_macro.OPENSSL_sk_value(ctx, i))
+ if ct == nil then
+ return nil
+ end
+ return ffi_str(ct)
+end
+
+function _M:get_ocsp_url(return_all)
+ local st = C.X509_get1_ocsp(self.ctx)
+
+ local count = stack_macro.OPENSSL_sk_num(st)
+ if count == 0 then
+ return
+ end
+
+ local ret
+ if return_all then
+ ret = {}
+ for i=0,count-1 do
+ ret[i+1] = OPENSSL_STRING_value_at(st, i)
+ end
+ else
+ ret = OPENSSL_STRING_value_at(st, 0)
+ end
+
+ C.X509_email_free(st)
+ return ret
+end
+
+function _M:get_ocsp_request()
+
+end
+
+function _M:get_crl_url(return_all)
+ local cdp, err = self:get_crl_distribution_points()
+ if err then
+ return nil, err
+ end
+
+ if not cdp or cdp:count() == 0 then
+ return
+ end
+
+ if return_all then
+ local ret = {}
+ local cdp_iter = cdp:each()
+ while true do
+ local _, gn = cdp_iter()
+ if not gn then
+ break
+ end
+ local gn_iter = gn:each()
+ while true do
+ local k, v = gn_iter()
+ if not k then
+ break
+ elseif k == "URI" then
+ table.insert(ret, v)
+ end
+ end
+ end
+ return ret
+ else
+ local gn, err = cdp:index(1)
+ if err then
+ return nil, err
+ end
+ local iter = gn:each()
+ while true do
+ local k, v = iter()
+ if not k then
+ break
+ elseif k == "URI" then
+ return v
+ end
+ end
+ end
+end
+
+local digest_length = ctypes.ptr_of_uint()
+local digest_buf, digest_buf_size
+local function digest(self, cfunc, typ, properties)
+ -- TODO: dedup the following with resty.openssl.digest
+ local ctx
+ if OPENSSL_11_OR_LATER then
+ ctx = C.EVP_MD_CTX_new()
+ ffi_gc(ctx, C.EVP_MD_CTX_free)
+ elseif OPENSSL_10 then
+ ctx = C.EVP_MD_CTX_create()
+ ffi_gc(ctx, C.EVP_MD_CTX_destroy)
+ end
+ if ctx == nil then
+ return nil, "x509:digest: failed to create EVP_MD_CTX"
+ end
+
+ local algo
+ if OPENSSL_3X then
+ algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), typ or 'sha1', properties)
+ else
+ algo = C.EVP_get_digestbyname(typ or 'sha1')
+ end
+ if algo == nil then
+ return nil, string.format("x509:digest: invalid digest type \"%s\"", typ)
+ end
+
+ local md_size = OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)
+ if not digest_buf or digest_buf_size < md_size then
+ digest_buf = ctypes.uchar_array(md_size)
+ digest_buf_size = md_size
+ end
+
+ if cfunc(self.ctx, algo, digest_buf, digest_length) ~= 1 then
+ return nil, format_error("x509:digest")
+ end
+
+ return ffi_str(digest_buf, digest_length[0])
+end
+
+function _M:digest(typ, properties)
+ return digest(self, C.X509_digest, typ, properties)
+end
+
+function _M:pubkey_digest(typ, properties)
+ return digest(self, C.X509_pubkey_digest, typ, properties)
+end
+
+function _M:check_private_key(key)
+ if not pkey_lib.istype(key) then
+ return false, "x509:check_private_key: except a pkey instance at #1"
+ end
+
+ if not key:is_private() then
+ return false, "x509:check_private_key: not a private key"
+ end
+
+ if C.X509_check_private_key(self.ctx, key.ctx) == 1 then
+ return true
+ end
+ return false, format_error("x509:check_private_key")
+end
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509:verify", code)
+ end
+end
+
+-- AUTO GENERATED
+local function get_extension(ctx, nid_txt, last_pos)
+ last_pos = (last_pos or 0) - 1
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+ local pos = C.X509_get_ext_by_NID(ctx, nid, last_pos)
+ if pos == -1 then
+ return nil
+ end
+ local ctx = C.X509_get_ext(ctx, pos)
+ if ctx == nil then
+ return nil, nil, format_error()
+ end
+ return ctx, pos
+end
+
+-- AUTO GENERATED
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:add_extension: expect a x509.extension instance at #1"
+ end
+
+ -- X509_add_ext returnes the stack on success, and NULL on error
+ -- the X509_EXTENSION ctx is dupped internally
+ if C.X509_add_ext(self.ctx, extension.ctx, -1) == nil then
+ return false, format_error("x509:add_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local X509_delete_ext
+if OPENSSL_11_OR_LATER then
+ X509_delete_ext = C.X509_delete_ext
+elseif OPENSSL_10 then
+ X509_delete_ext = function(ctx, pos)
+ return C.X509v3_delete_ext(ctx.cert_info.extensions, pos)
+ end
+else
+ X509_delete_ext = function(...)
+ error("X509_delete_ext undefined")
+ end
+end
+
+-- AUTO GENERATED
+function _M:set_extension(extension, last_pos)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ last_pos = (last_pos or 0) - 1
+
+ local nid = extension:get_object().nid
+ local pos = C.X509_get_ext_by_NID(self.ctx, nid, last_pos)
+ -- pos may be -1, which means not found, it's fine, we will add new one instead of replace
+
+ local removed = X509_delete_ext(self.ctx, pos)
+ C.X509_EXTENSION_free(removed)
+
+ if C.X509_add_ext(self.ctx, extension.ctx, pos) == nil then
+ return false, format_error("x509:set_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509:set_extension_critical: " .. err
+ end
+
+ if C.X509_EXTENSION_set_critical(ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509:set_extension_critical")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- AUTO GENERATED
+function _M:get_serial_number()
+ local got = accessors.get_serial_number(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ -- returns a new BIGNUM instance
+ got = C.ASN1_INTEGER_to_BN(got, nil)
+ if got == nil then
+ return false, format_error("x509:set: BN_to_ASN1_INTEGER")
+ end
+ -- bn will be duplicated thus this ctx should be freed up
+ ffi_gc(got, C.BN_free)
+
+ local lib = require("resty.openssl.bn")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_serial_number(toset)
+ local lib = require("resty.openssl.bn")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_serial_number: expect a bn instance at #1"
+ end
+ toset = toset.ctx
+
+ toset = C.BN_to_ASN1_INTEGER(toset, nil)
+ if toset == nil then
+ return false, format_error("x509:set: BN_to_ASN1_INTEGER")
+ end
+ -- "A copy of the serial number is used internally
+ -- so serial should be freed up after use.""
+ ffi_gc(toset, C.ASN1_INTEGER_free)
+
+ if accessors.set_serial_number(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_serial_number")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_not_before()
+ local got = accessors.get_not_before(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_not_before(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_not_before: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_not_before(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_not_before")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_not_after()
+ local got = accessors.get_not_after(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_not_after(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_not_after: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_not_after(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_not_after")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_pubkey()
+ local got = accessors.get_pubkey(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.pkey")
+ -- returned a copied instance directly
+ return lib.new(got)
+end
+
+-- AUTO GENERATED
+function _M:set_pubkey(toset)
+ local lib = require("resty.openssl.pkey")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_pubkey: expect a pkey instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_pubkey(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_pubkey")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_subject_name()
+ local got = accessors.get_subject_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_subject_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_subject_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_subject_name(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_subject_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_issuer_name()
+ local got = accessors.get_issuer_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_issuer_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_issuer_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_issuer_name(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_issuer_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_version")
+ end
+ return true
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_subject_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_subject_alt_name: extension of subject_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_subject_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_subject_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_subject_alt_name, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_subject_alt_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_subject_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name_critical()
+ return _M.get_extension_critical(self, NID_subject_alt_name)
+end
+
+local NID_issuer_alt_name = C.OBJ_sn2nid("issuerAltName")
+assert(NID_issuer_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_issuer_alt_name()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_issuer_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_issuer_alt_name: extension of issuer_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_issuer_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_issuer_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_issuer_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_issuer_alt_name, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_issuer_alt_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_issuer_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_issuer_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_issuer_alt_name_critical()
+ return _M.get_extension_critical(self, NID_issuer_alt_name)
+end
+
+local NID_basic_constraints = C.OBJ_sn2nid("basicConstraints")
+assert(NID_basic_constraints ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_basic_constraints(name)
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_basic_constraints, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_basic_constraints: extension of basic_constraints occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_basic_constraints")
+ end
+
+ local ctx = ffi_cast("BASIC_CONSTRAINTS*", got)
+
+ local ca = ctx.ca == 0xFF
+ local pathlen = tonumber(C.ASN1_INTEGER_get(ctx.pathlen))
+
+ C.BASIC_CONSTRAINTS_free(ctx)
+
+ if not name or type(name) ~= "string" then
+ got = {
+ ca = ca,
+ pathlen = pathlen,
+ }
+ elseif string.lower(name) == "ca" then
+ got = ca
+ elseif string.lower(name) == "pathlen" then
+ got = pathlen
+ end
+
+ return got
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_basic_constraints(toset)
+ if type(toset) ~= "table" then
+ return false, "x509:set_basic_constraints: expect a table at #1"
+ end
+
+ local cfg_lower = {}
+ for k, v in pairs(toset) do
+ cfg_lower[string.lower(k)] = v
+ end
+
+ toset = C.BASIC_CONSTRAINTS_new()
+ if toset == nil then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS")
+ end
+ ffi_gc(toset, C.BASIC_CONSTRAINTS_free)
+
+ toset.ca = cfg_lower.ca and 0xFF or 0
+ local pathlen = cfg_lower.pathlen and tonumber(cfg_lower.pathlen)
+ if pathlen then
+ C.ASN1_INTEGER_free(toset.pathlen)
+
+ local asn1 = C.ASN1_STRING_type_new(pathlen)
+ if asn1 == nil then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS: ASN1_STRING_type_new")
+ end
+ toset.pathlen = asn1
+
+ local code = C.ASN1_INTEGER_set(asn1, pathlen)
+ if code ~= 1 then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS: ASN1_INTEGER_set", code)
+ end
+ end
+
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_basic_constraints, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_basic_constraints")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_basic_constraints_critical(crit)
+ return _M.set_extension_critical(self, NID_basic_constraints, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_basic_constraints_critical()
+ return _M.get_extension_critical(self, NID_basic_constraints)
+end
+
+local NID_info_access = C.OBJ_sn2nid("authorityInfoAccess")
+assert(NID_info_access ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_info_access()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_info_access, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_info_access: extension of info_access occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_info_access")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a ACCESS_DESCRIPTION
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("ACCESS_DESCRIPTION"))
+ got = ffi_cast("AUTHORITY_INFO_ACCESS*", got_ref)
+ local lib = require("resty.openssl.x509.extension.info_access")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_info_access(toset)
+ local lib = require("resty.openssl.x509.extension.info_access")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_info_access: expect a x509.extension.info_access instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_info_access, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_info_access")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_info_access_critical(crit)
+ return _M.set_extension_critical(self, NID_info_access, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_info_access_critical()
+ return _M.get_extension_critical(self, NID_info_access)
+end
+
+local NID_crl_distribution_points = C.OBJ_sn2nid("crlDistributionPoints")
+assert(NID_crl_distribution_points ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_crl_distribution_points()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_crl_distribution_points, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_crl_distribution_points: extension of crl_distribution_points occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_crl_distribution_points")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a DIST_POINT
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("DIST_POINT"))
+ got = ffi_cast("OPENSSL_STACK*", got_ref)
+ local lib = require("resty.openssl.x509.extension.dist_points")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_crl_distribution_points(toset)
+ local lib = require("resty.openssl.x509.extension.dist_points")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_crl_distribution_points: expect a x509.extension.dist_points instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_crl_distribution_points, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_crl_distribution_points")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_crl_distribution_points_critical(crit)
+ return _M.set_extension_critical(self, NID_crl_distribution_points, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_crl_distribution_points_critical()
+ return _M.get_extension_critical(self, NID_crl_distribution_points)
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
diff --git a/server/resty/openssl/x509/name.lua b/server/resty/openssl/x509/name.lua
new file mode 100644
index 0000000..f83fcc1
--- /dev/null
+++ b/server/resty/openssl/x509/name.lua
@@ -0,0 +1,156 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509.name"
+require "resty.openssl.include.err"
+local objects_lib = require "resty.openssl.objects"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+-- local MBSTRING_FLAG = 0x1000
+local MBSTRING_ASC = 0x1001 -- (MBSTRING_FLAG|1)
+
+local _M = {}
+
+local x509_name_ptr_ct = ffi.typeof("X509_NAME*")
+
+-- starts from 0
+local function value_at(ctx, i)
+ local entry = C.X509_NAME_get_entry(ctx, i)
+ local obj = C.X509_NAME_ENTRY_get_object(entry)
+ local ret = objects_lib.obj2table(obj)
+
+ local str = C.X509_NAME_ENTRY_get_data(entry)
+ if str ~= nil then
+ ret.blob = ffi_str(asn1_macro.ASN1_STRING_get0_data(str))
+ end
+
+ return ret
+end
+
+local function iter(tbl)
+ local i = 0
+ local n = tonumber(C.X509_NAME_entry_count(tbl.ctx))
+ return function()
+ i = i + 1
+ if i <= n then
+ local obj = value_at(tbl.ctx, i-1)
+ return obj.sn or obj.ln or obj.id, obj
+ end
+ end
+end
+
+local mt = {
+ __index = _M,
+ __pairs = iter,
+ __len = function(tbl) return tonumber(C.X509_NAME_entry_count(tbl.ctx)) end,
+}
+
+function _M.new()
+ local ctx = C.X509_NAME_new()
+ if ctx == nil then
+ return nil, "x509.name.new: X509_NAME_new() failed"
+ end
+ ffi_gc(ctx, C.X509_NAME_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_name_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_name_ptr_ct, ctx) then
+ return nil, "x509.name.dup: expect a x509.name ctx at #1, got " .. type(ctx)
+ end
+ local ctx = C.X509_NAME_dup(ctx)
+ ffi_gc(ctx, C.X509_NAME_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:add(nid, txt)
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, "x509.name:add: invalid NID text " .. (nid or "nil")
+ end
+
+ local code = C.X509_NAME_add_entry_by_OBJ(self.ctx, asn1, MBSTRING_ASC, txt, #txt, -1, 0)
+ C.ASN1_OBJECT_free(asn1)
+
+ if code ~= 1 then
+ return nil, "x509.name:add: X509_NAME_add_entry_by_OBJ() failed"
+ end
+
+ return self
+end
+
+function _M:find(nid, last_pos)
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, nil, "x509.name:find: invalid NID text " .. (nid or "nil")
+ end
+ -- make 1-index array to 0-index
+ last_pos = (last_pos or 0) - 1
+
+ local pos = C.X509_NAME_get_index_by_OBJ(self.ctx, asn1, last_pos)
+ if pos == -1 then
+ return nil
+ end
+
+ C.ASN1_OBJECT_free(asn1)
+
+ return value_at(self.ctx, pos), pos+1
+end
+
+-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
+function _M:all()
+ local ret = {}
+ local _next = iter(self)
+ while true do
+ local k, obj = _next()
+ if obj then
+ ret[k] = obj
+ else
+ break
+ end
+ end
+ return ret
+end
+
+function _M:each()
+ return iter(self)
+end
+
+mt.__tostring = function(self)
+ local values = {}
+ local _next = iter(self)
+ while true do
+ local k, v = _next()
+ if k then
+ table.insert(values, k .. "=" .. v.blob)
+ else
+ break
+ end
+ end
+ table.sort(values)
+ return table.concat(values, "/")
+end
+
+_M.tostring = mt.__tostring
+
+return _M
diff --git a/server/resty/openssl/x509/revoked.lua b/server/resty/openssl/x509/revoked.lua
new file mode 100644
index 0000000..9762200
--- /dev/null
+++ b/server/resty/openssl/x509/revoked.lua
@@ -0,0 +1,108 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.x509.crl"
+require "resty.openssl.include.x509.revoked"
+local bn_lib = require("resty.openssl.bn")
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+local mt = { __index = _M }
+
+local x509_revoked_ptr_ct = ffi.typeof('X509_REVOKED*')
+
+local NID_crl_reason = C.OBJ_txt2nid("CRLReason")
+assert(NID_crl_reason > 0)
+
+--- Creates new instance of X509_REVOKED data
+-- @tparam bn|number sn Serial number as number or bn instance
+-- @tparam number time Revocation time
+-- @tparam number reason Revocation reason
+-- @treturn table instance of the module or nil
+-- @treturn[opt] string Returns optional error message in case of error
+function _M.new(sn, time, reason)
+ --- only convert to bn if it is number
+ if type(sn) == "number"then
+ sn = bn_lib.new(sn)
+ end
+ if not bn_lib.istype(sn) then
+ return nil, "x509.revoked.new: sn should be number or a bn instance"
+ end
+
+ if type(time) ~= "number" then
+ return nil, "x509.revoked.new: expect a number at #2"
+ end
+ if type(reason) ~= "number" then
+ return nil, "x509.revoked.new: expect a number at #3"
+ end
+
+ local ctx = C.X509_REVOKED_new()
+ ffi_gc(ctx, C.X509_REVOKED_free)
+
+ -- serial number
+ local sn_asn1 = C.BN_to_ASN1_INTEGER(sn.ctx, nil)
+ if sn_asn1 == nil then
+ return nil, "x509.revoked.new: BN_to_ASN1_INTEGER() failed"
+ end
+ ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
+
+ if C.X509_REVOKED_set_serialNumber(ctx, sn_asn1) == 0 then
+ return nil, format_error("x509.revoked.new: X509_REVOKED_set_serialNumber()")
+ end
+
+ -- time
+ time = C.ASN1_TIME_set(nil, time)
+ if time == nil then
+ return nil, format_error("x509.revoked.new: ASN1_TIME_set()")
+ end
+ ffi_gc(time, C.ASN1_STRING_free)
+
+ if C.X509_REVOKED_set_revocationDate(ctx, time) == 0 then
+ return nil, format_error("x509.revoked.new: X509_REVOKED_set_revocationDate()")
+ end
+
+ -- reason
+ local reason_asn1 = C.ASN1_ENUMERATED_new()
+ if reason_asn1 == nil then
+ return nil, "x509.revoked.new: ASN1_ENUMERATED_new() failed"
+ end
+ ffi_gc(reason_asn1, C.ASN1_ENUMERATED_free)
+
+ local reason_ext = C.X509_EXTENSION_new()
+ if reason_ext == nil then
+ return nil, "x509.revoked.new: X509_EXTENSION_new() failed"
+ end
+ ffi_gc(reason_ext, C.X509_EXTENSION_free)
+
+ if C.ASN1_ENUMERATED_set(reason_asn1, reason) == 0 then
+ return nil, format_error("x509.revoked.new: ASN1_ENUMERATED_set()")
+ end
+
+ if C.X509_EXTENSION_set_data(reason_ext, reason_asn1) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_data()")
+ end
+
+ if C.X509_EXTENSION_set_object(reason_ext, C.OBJ_nid2obj(NID_crl_reason)) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
+ end
+
+ if C.X509_REVOKED_add_ext(ctx, reason_ext, 0) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+--- Type check
+-- @tparam table Instance of revoked module
+-- @treturn boolean true if instance is instance of revoked module false otherwise
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_revoked_ptr_ct, l.ctx)
+end
+
+return _M
diff --git a/server/resty/openssl/x509/store.lua b/server/resty/openssl/x509/store.lua
new file mode 100644
index 0000000..1722e4c
--- /dev/null
+++ b/server/resty/openssl/x509/store.lua
@@ -0,0 +1,227 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+local bor = bit.bor
+
+local x509_vfy_macro = require "resty.openssl.include.x509_vfy"
+local x509_lib = require "resty.openssl.x509"
+local chain_lib = require "resty.openssl.x509.chain"
+local crl_lib = require "resty.openssl.x509.crl"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_all_error
+local format_all_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = { __index = _M }
+
+_M.verify_flags = x509_vfy_macro.verify_flags
+
+local x509_store_ptr_ct = ffi.typeof('X509_STORE*')
+
+function _M.new()
+ local ctx = C.X509_STORE_new()
+ if ctx == nil then
+ return nil, "x509.store.new: X509_STORE_new() failed"
+ end
+ ffi_gc(ctx, C.X509_STORE_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_store_ptr_ct, l.ctx)
+end
+
+function _M:use_default(properties)
+ if x509_vfy_macro.X509_STORE_set_default_paths(self.ctx, ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:use_default")
+ end
+ return true
+end
+
+function _M:add(item)
+ local dup
+ local err
+ if x509_lib.istype(item) then
+ dup = C.X509_dup(item.ctx)
+ if dup == nil then
+ return false, "x509.store:add: X509_dup() failed"
+ end
+ -- ref counter of dup is increased by 1
+ if C.X509_STORE_add_cert(self.ctx, dup) ~= 1 then
+ err = format_all_error("x509.store:add: X509_STORE_add_cert")
+ end
+ -- decrease the dup ctx ref count immediately to make leak test happy
+ C.X509_free(dup)
+ elseif crl_lib.istype(item) then
+ dup = C.X509_CRL_dup(item.ctx)
+ if dup == nil then
+ return false, "x509.store:add: X509_CRL_dup() failed"
+ end
+ -- ref counter of dup is increased by 1
+ if C.X509_STORE_add_crl(self.ctx, dup) ~= 1 then
+ err = format_all_error("x509.store:add: X509_STORE_add_crl")
+ end
+
+ -- define X509_V_FLAG_CRL_CHECK 0x4
+ -- enables CRL checking for the certificate chain leaf certificate.
+ -- An error occurs if a suitable CRL cannot be found.
+ -- Note: this does not check for certificates in the chain.
+ if C.X509_STORE_set_flags(self.ctx, 0x4) ~= 1 then
+ return false, format_error("x509.store:add: X509_STORE_set_flags")
+ end
+ -- decrease the dup ctx ref count immediately to make leak test happy
+ C.X509_CRL_free(dup)
+ else
+ return false, "x509.store:add: expect an x509 or crl instance at #1"
+ end
+
+ if err then
+ return false, err
+ end
+
+ -- X509_STORE doesn't have stack gc handler, we need to gc by ourselves
+ self._elem_refs[self._elem_refs_idx] = dup
+ self._elem_refs_idx = self._elem_refs_idx + 1
+
+ return true
+end
+
+function _M:load_file(path, properties)
+ if type(path) ~= "string" then
+ return false, "x509.store:load_file: expect a string at #1"
+ else
+ if x509_vfy_macro.X509_STORE_load_locations(self.ctx, path, nil,
+ ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:load_file")
+ end
+ end
+
+ return true
+end
+
+function _M:load_directory(path, properties)
+ if type(path) ~= "string" then
+ return false, "x509.store:load_directory expect a string at #1"
+ else
+ if x509_vfy_macro.X509_STORE_load_locations(self.ctx, nil, path,
+ ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:load_directory")
+ end
+ end
+
+ return true
+end
+
+function _M:set_depth(depth)
+ depth = depth and tonumber(depth)
+ if not depth then
+ return nil, "x509.store:set_depth: expect a number at #1"
+ end
+
+ if C.X509_STORE_set_depth(self.ctx, depth) ~= 1 then
+ return false, format_error("x509.store:set_depth")
+ end
+
+ return true
+end
+
+function _M:set_purpose(purpose)
+ if type(purpose) ~= "string" then
+ return nil, "x509.store:set_purpose: expect a string at #1"
+ end
+
+ local pchar = ffi.new("char[?]", #purpose, purpose)
+ local idx = C.X509_PURPOSE_get_by_sname(pchar)
+ idx = tonumber(idx)
+
+ if idx == -1 then
+ return false, "invalid purpose \"" .. purpose .. "\""
+ end
+
+ local purp = C.X509_PURPOSE_get0(idx)
+ local i = C.X509_PURPOSE_get_id(purp)
+
+ if C.X509_STORE_set_purpose(self.ctx, i) ~= 1 then
+ return false, format_error("x509.store:set_purpose: X509_STORE_set_purpose")
+ end
+
+ return true
+end
+
+function _M:set_flags(...)
+ local flag = 0
+ for _, f in ipairs({...}) do
+ flag = bor(flag, f)
+ end
+
+ if C.X509_STORE_set_flags(self.ctx, flag) ~= 1 then
+ return false, format_error("x509.store:set_flags: X509_STORE_set_flags")
+ end
+
+ return true
+end
+
+function _M:verify(x509, chain, return_chain, properties, verify_method)
+ if not x509_lib.istype(x509) then
+ return nil, "x509.store:verify: expect a x509 instance at #1"
+ elseif chain and not chain_lib.istype(chain) then
+ return nil, "x509.store:verify: expect a x509.chain instance at #1"
+ end
+
+ local ctx
+ if OPENSSL_3X then
+ ctx = C.X509_STORE_CTX_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_STORE_CTX_new()
+ end
+ if ctx == nil then
+ return nil, "x509.store:verify: X509_STORE_CTX_new() failed"
+ end
+
+ ffi_gc(ctx, C.X509_STORE_CTX_free)
+
+ local chain_dup_ctx
+ if chain then
+ local chain_dup, err = chain_lib.dup(chain.ctx)
+ if err then
+ return nil, err
+ end
+ chain_dup_ctx = chain_dup.ctx
+ end
+
+ if C.X509_STORE_CTX_init(ctx, self.ctx, x509.ctx, chain_dup_ctx) ~= 1 then
+ return nil, format_error("x509.store:verify: X509_STORE_CTX_init")
+ end
+
+ if verify_method and C.X509_STORE_CTX_set_default(ctx, verify_method) ~= 1 then
+ return nil, "x509.store:verify: invalid verify_method \"" .. verify_method .. "\""
+ end
+
+ local code = C.X509_verify_cert(ctx)
+ if code == 1 then -- verified
+ if not return_chain then
+ return true, nil
+ end
+ local ret_chain_ctx = x509_vfy_macro.X509_STORE_CTX_get0_chain(ctx)
+ return chain_lib.dup(ret_chain_ctx)
+ elseif code == 0 then -- unverified
+ local vfy_code = C.X509_STORE_CTX_get_error(ctx)
+
+ return nil, ffi_str(C.X509_verify_cert_error_string(vfy_code))
+ end
+
+ -- error
+ return nil, format_error("x509.store:verify: X509_verify_cert", code)
+
+end
+
+return _M