refactor - fix various errors and add missing dependencies

This commit is contained in:
florian 2023-04-17 18:51:02 +02:00
parent 928ed2d6ce
commit 8d63e39740
No known key found for this signature in database
GPG Key ID: 3D80806F12602A7C
204 changed files with 30806 additions and 550 deletions

5
TODO Normal file
View File

@ -0,0 +1,5 @@
- utils refactoring
- use resty.template for antibot
- load inline values for white/black/grey list core
- check if correct setting is set to yes in new() before loading stuff in self
- store object in ngx.ctx

View File

@ -1,38 +1,46 @@
local class = require "middleclass"
local datastore = require "bunkerweb.datastore"
local utils = require "bunkerweb.utils"
local cjson = require "cjson"
local upload = require "resty.upload"
local api = { global = { GET = {}, POST = {}, PUT = {}, DELETE = {} } }
local api = class("api")
api.response = function(self, http_status, api_status, msg)
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
function api:initialize()
self.datastore = datastore:new()
end
function api:response(http_status, api_status, msg)
local resp = {}
resp["status"] = api_status
resp["msg"] = msg
return http_status, resp
end
api.global.GET["^/ping$"] = function(api)
return api:response(ngx.HTTP_OK, "success", "pong")
api.global.GET["^/ping$"] = function(self)
return self:response(ngx.HTTP_OK, "success", "pong")
end
api.global.POST["^/reload$"] = function(api)
api.global.POST["^/reload$"] = function(self)
local status = os.execute("nginx -s reload")
if status == 0 then
return api:response(ngx.HTTP_OK, "success", "reload successful")
return self:response(ngx.HTTP_OK, "success", "reload successful")
end
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
end
api.global.POST["^/stop$"] = function(api)
api.global.POST["^/stop$"] = function(self)
local status = os.execute("nginx -s quit")
if status == 0 then
return api:response(ngx.HTTP_OK, "success", "stop successful")
return self:response(ngx.HTTP_OK, "success", "stop successful")
end
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
end
api.global.POST["^/confs$"] = function(api)
api.global.POST["^/confs$"] = function(self)
local tmp = "/var/tmp/bunkerweb/api_" .. ngx.var.uri:sub(2) .. ".tar.gz"
local destination = "/usr/share/bunkerweb/" .. ngx.var.uri:sub(2)
if ngx.var.uri == "/confs" then
@ -48,7 +56,7 @@ api.global.POST["^/confs$"] = function(api)
end
local form, err = upload:new(4096)
if not form then
return api:response(ngx.HTTP_BAD_REQUEST, "error", err)
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
end
form:set_timeout(1000)
local file = io.open(tmp, "w+")
@ -56,7 +64,7 @@ api.global.POST["^/confs$"] = function(api)
local typ, res, err = form:read()
if not typ then
file:close()
return api:response(ngx.HTTP_BAD_REQUEST, "error", err)
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
end
if typ == "eof" then
break
@ -69,13 +77,13 @@ api.global.POST["^/confs$"] = function(api)
file:close()
local status = os.execute("rm -rf " .. destination .. "/*")
if status ~= 0 then
return api:response(ngx.HTTP_BAD_REQUEST, "error", "can't remove old files")
return self:response(ngx.HTTP_BAD_REQUEST, "error", "can't remove old files")
end
status = os.execute("tar xzf " .. tmp .. " -C " .. destination)
if status ~= 0 then
return api:response(ngx.HTTP_BAD_REQUEST, "error", "can't extract archive")
return self:response(ngx.HTTP_BAD_REQUEST, "error", "can't extract archive")
end
return api:response(ngx.HTTP_OK, "success", "saved data at " .. destination)
return self:response(ngx.HTTP_OK, "success", "saved data at " .. destination)
end
api.global.POST["^/data$"] = api.global.POST["^/confs$"]
@ -86,7 +94,7 @@ api.global.POST["^/custom_configs$"] = api.global.POST["^/confs$"]
api.global.POST["^/plugins$"] = api.global.POST["^/confs$"]
api.global.POST["^/unban$"] = function(api)
api.global.POST["^/unban$"] = function(self)
ngx.req.read_body()
local data = ngx.req.get_body_data()
if not data then
@ -99,13 +107,13 @@ api.global.POST["^/unban$"] = function(api)
end
local ok, ip = pcall(cjson.decode, data)
if not ok then
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. env)
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. env)
end
datastore:delete("bans_ip_" .. ip["ip"])
return api:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
self.datastore:delete("bans_ip_" .. ip["ip"])
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
end
api.global.POST["^/ban$"] = function(api)
api.global.POST["^/ban$"] = function(self)
ngx.req.read_body()
local data = ngx.req.get_body_data()
if not data then
@ -118,45 +126,45 @@ api.global.POST["^/ban$"] = function(api)
end
local ok, ip = pcall(cjson.decode, data)
if not ok then
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. env)
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. env)
end
datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
return api:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
self.datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
end
api.global.GET["^/bans$"] = function(api)
api.global.GET["^/bans$"] = function(self)
local data = {}
for i, k in ipairs(datastore:keys()) do
for i, k in ipairs(self.datastore:keys()) do
if k:find("^bans_ip_") then
local ret, reason = datastore:get(k)
if not ret then
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
"can't access " .. k .. " from datastore : " + reason)
end
local ret, exp = datastore:exp(k)
local ret, exp = self.datastore:exp(k)
if not ret then
return api:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
"can't access exp " .. k .. " from datastore : " + exp)
end
local ban = { ip = k:sub(9, #k), reason = reason, exp = exp }
table.insert(data, ban)
end
end
return api:response(ngx.HTTP_OK, "success", data)
return self:response(ngx.HTTP_OK, "success", data)
end
api.is_allowed_ip = function(self)
local data, err = datastore:get("api_whitelist_ip")
function api:is_allowed_ip()
local data, err = self.datastore:get("api_whitelist_ip")
if not data then
return false, "can't access api_allowed_ips in datastore"
end
if utils.is_ip_in_networks(ngx.var.remote_addr, cjson.decode(data).data) then
if utils.is_ip_in_networks(ngx.var.remote_addr, cjson.decode(data)) then
return true, "ok"
end
return false, "IP is not in API_WHITELIST_IP"
end
api.do_api_call = function(self)
function api:do_api_call()
if self.global[ngx.var.request_method] ~= nil then
for uri, api_fun in pairs(self.global[ngx.var.request_method]) do
if string.match(ngx.var.uri, uri) then
@ -175,7 +183,7 @@ api.do_api_call = function(self)
end
end
end
local list, err = datastore:get("plugins")
local list, err = self.datastore:get("plugins")
if not list then
local status, resp = self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
return false, resp["msg"], ngx.HTTP_INTERNAL_SERVER_ERROR, resp

View File

@ -35,15 +35,15 @@ local cache, err = mlcache.new(
ipc_shm = ipc_shm
}
)
logger:new("CACHESTORE")
local module_logger = logger:new("CACHESTORE")
if not cache then
logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
end
function cachestore:new(use_redis)
function cachestore:initialize(use_redis)
self.cache = cache
self.use_redis = use_redis or false
self.logger = logger
self.logger = module_logger
end
function cachestore:get(key)
@ -96,7 +96,7 @@ function cachestore:get(key)
else
value, err, hit_level = self.cache:get(key)
end
if err then
if value == nil and hit_level == nil then
return false, err
end
self.logger:log(ngx.INFO, "hit level for " .. key .. " = " .. tostring(hit_level))

View File

@ -1,10 +1,13 @@
local class = require "middleclass"
local utils = require "bunkerweb.utils"
local logger = require "bunkerweb.logger"
local redis = require "resty.redis"
local clusterstore = class("clusterstore")
function clusterstore:new()
function clusterstore:initialize()
-- Instantiate logger
self.logger = logger:new("CLUSTERSTORE")
-- Get variables
local variables = {
["REDIS_HOST"] = "",
@ -20,16 +23,19 @@ function clusterstore:new()
for k, v in pairs(variables) do
local value, err = utils.get_variable(k, false)
if value == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.variables[k] = value
end
-- Don't instantiate a redis object for now
self.redis_client = nil
return true, "success"
end
function clusterstore:connect()
-- Check if we are already connected
if self.redis_client ~= nil then
return true, "already connected"
end
-- Instantiate object
local redis_client, err = redis:new()
if redis_client == nil then
@ -47,24 +53,28 @@ function clusterstore:connect()
if not ok then
return false, err
end
-- Save client
self.redis_client = redis_client
-- Select database if needed
local times, err = redis_client:get_reused_times()
if err then
self:close()
return false, err
end
if times == 0 then
local select, err = redis_client:select(tonumber(variables["REDIS_DATABASE"]))
if err then
self:close()
return false, err
end
end
self.redis_client = redis_client
return return true, "success"
return true, "success"
end
function clusterstore:close()
if self.redis_client then
-- Equivalent to close but keep a pool of connections
self.redis_client = nil
return self.redis_client:set_keepalive(tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]), tonumber(self.variables["REDIS_KEEPALIVE_POOL"]))
end
return false, "not connected"

View File

@ -1,7 +1,7 @@
local class = require "middleclass"
local datastore = class("datastore")
function datastore:new()
function datastore:initialize()
self.dict = ngx.shared.datastore
if not self.dict then
self.dict = ngx.shared.datastore_stream
@ -16,21 +16,21 @@ function datastore:get(key)
return value, err
end
function datastore:set(self, key, value, exptime)
function datastore:set(key, value, exptime)
exptime = exptime or 0
return self.dict:safe_set(key, value, exptime)
end
function datastore:delete(self, key)
function datastore:delete(key)
self.dict:delete(key)
return true, "success"
end
function datastore:keys(self)
function datastore:keys()
return self.dict:get_keys(0)
end
function datastore:exp(self, key)
function datastore:exp(key)
local ttl, err = self.dict:ttl(key)
if not ttl then
return false, err
@ -38,7 +38,7 @@ function datastore:exp(self, key)
return true, ttl
end
function datastore:delete_all(self, pattern)
function datastore:delete_all(pattern)
local keys = self.dict:get_keys(0)
for i, key in ipairs(keys) do
if key:match(pattern) then

View File

@ -27,10 +27,10 @@ helpers.load_plugin = function(json)
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
end
-- Return plugin
return plugin
return true, plugin
end
helpers.new_plugin = function(id)
helpers.require_plugin = function(id)
-- Require call
local ok, plugin_lua = pcall(require, id .. "/" .. id)
if not ok then
@ -43,14 +43,19 @@ helpers.new_plugin = function(id)
if plugin_lua.new == nil then
return false, "missing new() method for plugin " .. id
end
local ok, err = pcall(plugin_lua.new, plugin_lua)
if not ok then
return false, "new() call failed for plugin " .. id .. " : " .. err
end
-- Return plugin
return plugin_lua, "new() call successful for plugin " .. id
end
helpers.new_plugin = function(plugin_lua)
-- Require call
local ok, plugin_obj = pcall(plugin_lua.new, plugin_lua)
if not ok then
return false, "new error for plugin " .. plugin_lua.name .. " : " .. plugin_obj
end
return true, plugin_obj
end
helpers.call_plugin = function(plugin, method)
-- Check if method is present
if plugin[method] == nil then
@ -59,7 +64,10 @@ helpers.call_plugin = function(plugin, method)
-- Call method
local ok, ret = pcall(plugin[method], plugin)
if not ok then
return false, plugin.id .. ":" .. method .. "() failed : " .. ret
return false, plugin:get_id() .. ":" .. method .. "() failed : " .. ret
end
if ret == nil then
return false, plugin:get_id() .. ":" .. method .. "() returned nil value"
end
-- Check values
local missing_values = {}

View File

@ -2,8 +2,8 @@ local errlog = require "ngx.errlog"
local class = require "middleclass"
local logger = class("logger")
function logger:new(prefix)
self.prefix = prefix
function logger:initialize(prefix)
self.prefix = string.upper(prefix)
end
function logger:log(level, msg)

View File

@ -1,32 +1,32 @@
local class = require "middleclass"
local logger = require "bunkerweb.logger"
local datastore = require "bunkerweb.datastore"
local datastore = require "bunkerweb.utils"
local utils = require "bunkerweb.utils"
local cjson = require "cjson"
local plugin = class("plugin")
function plugin:new(id)
function plugin:initialize(id)
-- Store default values
self.id = id
self.variables = {}
-- Instantiate logger
self.logger = require "bunkerweb.logger"
self.logger:new(id)
-- Instantiate objects
self.logger = logger:new(id)
self.datastore = datastore:new()
-- Get metadata
local encoded_metadata, err = datastore:get("plugin_" .. id)
local encoded_metadata, err = self.datastore:get("plugin_" .. id)
if not encoded_metadata then
return false, err
self.logger:log(ngx.ERR, err)
return
end
-- Store variables
local metadata = cjson.decode(encoded_metadata)
for k, v in pairs(metadata.settings) do
local value, err = utils.get_variable(k, v.context == "multisite")
local value, err = utils.get_variable(k, v.context == "multisite" and ngx.get_phase() ~= "init")
if value == nil then
return false, "can't get " .. k .. " variable : " .. err
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
end
self.variables[k] = value
end
return true, "success"
end
function plugin:get_id()

View File

@ -1,12 +1,13 @@
local datastore = require "bunkerweb.datastore"
local cdatastore = require "bunkerweb.datastore"
local ipmatcher = require "resty.ipmatcher"
local cjson = require "cjson"
local resolver = require "resty.dns.resolver"
local mmdb = require "bunkerweb.mmdb"
local logger = require "bunkerweb.logger"
local clogger = require "bunkerweb.logger"
local session = require "resty.session"
logger:new("UTILS")
local logger = clogger:new("UTILS")
local datastore = cdatastore:new()
local utils = {}
@ -176,7 +177,7 @@ utils.ip_is_global = function(ip)
if not ok then
return nil, "can't decode json : " .. reserved_ips
end
local ipm, err = ipmatcher.new(reserved_ips.data)
local ipm, err = ipmatcher.new(reserved_ips)
if not ipm then
return nil, "can't instantiate ipmatcher : " .. err
end
@ -418,4 +419,4 @@ utils.get_session = function(key)
return false, "no session"
end
return utils
return utils

View File

@ -15,24 +15,25 @@ server {
# check IP and do the API call
access_by_lua_block {
local api = require "bunkerweb.api"
local logger = require "bunkerweb.logger"
logger:new("API")
local capi = require "bunkerweb.api"
local clogger = require "bunkerweb.logger"
local logger = clogger:new("API")
local api = capi:new()
if not ngx.var.http_host or ngx.var.http_host ~= "{{ API_SERVER_NAME }}" then
logger.log(ngx.WARN, "wrong Host header from IP " .. ngx.var.remote_addr)
logger:log(ngx.WARN, "wrong Host header from IP " .. ngx.var.remote_addr)
return ngx.exit(ngx.HTTP_CLOSE)
end
local ok, err = api:is_allowed_ip()
if not ok then
logger.log(ngx.WARN, "can't validate access from IP " .. ngx.var.remote_addr .. " : " .. err)
logger:log(ngx.WARN, "can't validate access from IP " .. ngx.var.remote_addr .. " : " .. err)
return ngx.exit(ngx.HTTP_CLOSE)
end
logger.log(ngx.NOTICE, "validated access from IP " .. ngx.var.remote_addr)
logger:log(ngx.NOTICE, "validated access from IP " .. ngx.var.remote_addr)
local ok, err, status, resp = api:do_api_call()
if not ok then
logger.log(ngx.WARN, "call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " failed : " .. err)
logger:log(ngx.WARN, "call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " failed : " .. err)
else
logger.log(ngx.NOTICE, "successful call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " : " .. err)
logger:log(ngx.NOTICE, "successful call from " .. ngx.var.remote_addr .. " on " .. ngx.var.uri .. " : " .. err)
end
ngx.status = status
ngx.say(resp)

View File

@ -1,49 +1,50 @@
init_by_lua_block {
local class = require "middleclass"
local clogger = require "bunkerweb.logger"
local logger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local datastore = require "bunkerweb.datastore"
local cjson = require "cjson"
-- Start init phase
logger:new("INIT")
datastore:new()
logger:log(ngx.NOTICE, "init phase started")
local init_logger = logger:new("INIT")
local ds = datastore:new()
init_logger:log(ngx.NOTICE, "init phase started")
-- Remove previous data from the datastore
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
init_logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
local data_keys = {"^plugin_", "^variable_", "^plugins$", "^api_", "^misc_"}
for i, key in pairs(data_keys) do
local ok, err = datastore:delete_all(key)
local ok, err = ds:delete_all(key)
if not ok then
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
init_logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
return false
end
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
init_logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
end
logger:log(ngx.NOTICE, "deleted old keys from datastore")
init_logger:log(ngx.NOTICE, "deleted old keys from datastore")
-- Load variables into the datastore
logger:log(ngx.NOTICE, "saving variables into datastore ...")
init_logger:log(ngx.NOTICE, "saving variables into datastore ...")
local file = io.open("/etc/nginx/variables.env")
if not file then
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
init_logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
return false
end
file:close()
for line in io.lines("/etc/nginx/variables.env") do
local variable, value = line:match("(.+)=(.*)")
local ok, err = datastore:set("variable_" .. variable, value)
local ok, err = ds:set("variable_" .. variable, value)
if not ok then
logger:log(ngx.ERR, "can't save variable " .. variable .. " into datastore : " .. err)
init_logger:log(ngx.ERR, "can't save variable " .. variable .. " into datastore : " .. err)
return false
end
logger:log(ngx.INFO, "saved variable " .. variable .. "=" .. value .. " into datastore")
init_logger:log(ngx.INFO, "saved variable " .. variable .. "=" .. value .. " into datastore")
end
logger:log(ngx.NOTICE, "saved variables into datastore")
init_logger:log(ngx.NOTICE, "saved variables into datastore")
-- Set misc values into the datastore
logger:log(ngx.NOTICE, "saving misc values into datastore ...")
init_logger:log(ngx.NOTICE, "saving misc values into datastore ...")
local miscs = {
reserved_ips = {
"0.0.0.0/8",
@ -65,107 +66,112 @@ local miscs = {
},
resolvers = {}
}
local var_resolvers, err = datastore:get("variable_DNS_RESOLVERS")
local var_resolvers, err = ds:get("variable_DNS_RESOLVERS")
if not var_resolvers then
logger:log(ngx.ERR, "can't get variable DNS_RESOLVERS from datastore : " .. err)
init_logger:log(ngx.ERR, "can't get variable DNS_RESOLVERS from datastore : " .. err)
return false
end
for str_resolver in var_resolvers:gmatch("%S+") do
table.insert(miscs.resolvers, str_resolver)
end
for k, v in pairs(miscs) do
local ok, err = datastore:set("misc_" .. k, cjson.encode(v))
local ok, err = ds:set("misc_" .. k, cjson.encode(v))
if not ok then
logger:log(ngx.ERR, "can't save misc " .. k .. " into datastore : " .. err)
init_logger:log(ngx.ERR, "can't save misc " .. k .. " into datastore : " .. err)
return false
end
logger:log(ngx.INFO, "saved misc " .. k .. " into datastore")
init_logger:log(ngx.INFO, "saved misc " .. k .. " into datastore")
end
logger:log(ngx.NOTICE, "saved misc values into datastore")
init_logger:log(ngx.NOTICE, "saved misc values into datastore")
-- Set API values into the datastore
logger:log(ngx.NOTICE, "saving API values into datastore ...")
local value, err = datastore:get("variable_USE_API")
init_logger:log(ngx.NOTICE, "saving API values into datastore ...")
local value, err = ds:get("variable_USE_API")
if not value then
logger:log(ngx.ERR, "can't get variable USE_API from the datastore : " .. err)
init_logger:log(ngx.ERR, "can't get variable USE_API from the datastore : " .. err)
return false
end
if value == "yes" then
local value, err = datastore:get("variable_API_WHITELIST_IP")
local value, err = ds:get("variable_API_WHITELIST_IP")
if not value then
logger:log(ngx.ERR, "can't get variable API_WHITELIST_IP from the datastore : " .. err)
init_logger:log(ngx.ERR, "can't get variable API_WHITELIST_IP from the datastore : " .. err)
return false
end
local whitelists = {}
for whitelist in value:gmatch("%S+") do
table.insert(whitelists, whitelist)
end
local ok, err = datastore:set("api_whitelist_ip", cjson.encode(whitelists))
local ok, err = ds:set("api_whitelist_ip", cjson.encode(whitelists))
if not ok then
logger:log(ngx.ERR, "can't save API whitelist_ip to datastore : " .. err)
init_logger:log(ngx.ERR, "can't save API whitelist_ip to datastore : " .. err)
return false
end
logger:log(ngx.INFO, "saved API whitelist_ip into datastore")
init_logger:log(ngx.INFO, "saved API whitelist_ip into datastore")
end
logger:log(ngx.NOTICE, "saved API values into datastore")
init_logger:log(ngx.NOTICE, "saved API values into datastore")
-- Load plugins into the datastore
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
init_logger:log(ngx.NOTICE, "saving plugins into datastore ...")
local plugins = {}
local plugin_paths = {"/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins"}
for i, plugin_path in ipairs(plugin_paths) do
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
for path in paths:lines() do
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
if ok then
logger:log(ngx.ERR, err)
if not ok then
init_logger:log(ngx.ERR, plugin)
else
local ok, err = datastore:set("plugin_" .. plugin.id, cjson.encode(plugin))
local ok, err = ds:set("plugin_" .. plugin.id, cjson.encode(plugin))
if not ok then
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
init_logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
else
table.insert(plugins, plugin)
table.sort(plugins, function (a, b)
return a.order < b.order
end)
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
init_logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
end
end
end
end
local ok, err = datastore:set("plugins", cjson.encode(plugins))
local ok, err = ds:set("plugins", cjson.encode(plugins))
if not ok then
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
init_logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
return false
end
logger:log(ngx.NOTICE, "saved plugins into datastore")
init_logger:log(ngx.NOTICE, "saved plugins into datastore")
-- Call init() methods
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
init_logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
for i, plugin in ipairs(plugins) do
local plugin_lua, err = helpers.new_plugin(plugin.id)
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
init_logger:log(ngx.ERR, err)
elseif plugin_lua == nil then
init_logger:log(ngx.NOTICE, err)
else
logger:log(ngx.NOTICE, err)
end
if plugin_lua ~= nil then
local ok, ret = helpers.call_plugin(plugin_lua, "init")
if ok == false then
logger:log(ngx.ERR, ret)
elseif ok == nil then
logger:log(ngx.NOTICE, ret)
else
if ret.ret then
logger:log(ngx.NOTICE, plugin.id .. ":init() call successful : " .. ret.msg)
-- Check if plugin has init method
if plugin_lua.init ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
init_logger:log(ngx.ERR, plugin_obj)
else
logger:log(ngx.ERR, plugin.id .. ":init() call failed : " .. ret.msg)
local ok, ret = helpers.call_plugin(plugin_obj, "init")
if not ok then
init_logger:log(ngx.ERR, ret)
else
init_logger:log(ngx.NOTICE, plugin.id .. ":init() call successful : " .. ret.msg)
end
end
else
init_logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
end
end
end
logger:log(ngx.NOTICE, "called init() methods of plugins")
init_logger:log(ngx.NOTICE, "called init() methods of plugins")
logger:log(ngx.NOTICE, "init phase ended")
init_logger:log(ngx.NOTICE, "init phase ended")
}

View File

@ -3,25 +3,39 @@ access_by_lua_block {
local class = require "middleclass"
local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local datastore = require "bunkerweb.datastore"
local utils = require "bunkerweb.utils"
local cdatastore = require "bunkerweb.datastore"
local ccachestore = require "bunkerweb.cachestore"
local cjson = require "cjson"
-- Don't process internal requests
logger:new("ACCESS")
local logger = clogger:new("ACCESS")
if ngx.req.is_internal() then
logger:log(ngx.INFO, "skipped access phase because request is internal")
return true
end
-- Start access phase
datastore:new()
local datastore = cdatastore:new()
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
logger:log(ngx.ERR, err)
end
local cachestore = ccachestore:new(use_redis == "yes")
logger:log(ngx.INFO, "access phase started")
-- Update cachestore only once and before any other code
local ok, err = cachestore.cache:update()
if not ok then
logger:log(ngx.ERR, "can't update cachestore : " .. err)
end
-- Process bans as soon as possible
local ok, reason = cachestore:get("bans_ip_" .. ngx.var.remote_addr)
if not ok and reason then
logger:log(ngx.INFO, "error while checking if client is banned : " .. reason)
return false
else reason then
elseif reason then
logger:log(ngx.WARN, "IP " .. ngx.var.remote_addr .. " is banned with reason : " .. reason)
return ngx.exit(utils.get_deny_status())
end
@ -32,25 +46,31 @@ if not plugins then
logger:log(ngx.ERR, "can't get plugins from datastore : " .. err)
return false
end
plugins = cjson.decode(plugins)
-- Call access() methods
logger:log(ngx.INFO, "calling access() methods of plugins ...")
for i, plugin in ipairs(plugins) do
local plugin_lua, err = helpers.new_plugin(plugin.id)
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
else
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
end
if plugin_lua ~= nil then
local ok, ret = helpers.call_plugin(plugin_lua, "access")
if ok == false then
logger:log(ngx.ERR, ret)
elseif ok == nil then
logger:log(ngx.INFO, ret)
else
if ret.ret then
logger:log(ngx.INFO, plugin.id .. ":access() call successful : " .. ret.msg)
else
-- Check if plugin has access method
if plugin_lua.access ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "access")
if not ok then
logger:log(ngx.ERR, ret)
else
logger:log(ngx.INFO, plugin.id .. ":access() call successful : " .. ret.msg)
end
if ret.status then
if ret.status == utils.get_deny_status() then
ngx.ctx.reason = plugin.id
@ -63,10 +83,11 @@ for i, plugin in ipairs(plugins) do
elseif ret.redirect then
logger:log(ngx.NOTICE, plugin.id .. " redirect to " .. ret.redirect .. " : " .. err)
ngx.ctx.redirect = ret.redirect
break
end
else
logger:log(ngx.ERR, plugin.id .. ":access() call failed : " .. ret.msg)
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin.id .. " because method access() is not defined")
end
end
end

View File

@ -0,0 +1,61 @@
header_filter_by_lua_block {
local class = require "middleclass"
local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local cdatastore = require "bunkerweb.datastore"
local cjson = require "cjson"
-- Don't process internal requests
local logger = clogger:new("HEADER")
if ngx.req.is_internal() then
logger:log(ngx.INFO, "skipped header phase because request is internal")
return true
end
-- Start set phase
local datastore = cdatastore:new()
logger:log(ngx.INFO, "header phase started")
-- Get plugins
local plugins, err = datastore:get("plugins")
if not plugins then
logger:log(ngx.ERR, "can't get plugins from datastore : " .. err)
return false
end
plugins = cjson.decode(plugins)
-- Call header() methods
logger:log(ngx.INFO, "calling header() methods of plugins ...")
for i, plugin in ipairs(plugins) do
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
else
-- Check if plugin has header method
if plugin_lua.header ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "header")
if not ok then
logger:log(ngx.ERR, ret)
else
logger:log(ngx.INFO, plugin.id .. ":header() call successful : " .. ret.msg)
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin.id .. " because method header() is not defined")
end
end
end
logger:log(ngx.INFO, "called header() methods of plugins")
return true
}

View File

@ -3,11 +3,12 @@ log_by_lua_block {
local class = require "middleclass"
local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local datastore = require "bunkerweb.datastore"
local cdatastore = require "bunkerweb.datastore"
local cjson = require "cjson"
-- Start log phase
logger:new("LOG")
datastore:new()
local logger = clogger:new("LOG")
local datastore = cdatastore:new()
logger:log(ngx.INFO, "log phase started")
-- Get plugins
@ -16,28 +17,34 @@ if not plugins then
logger:log(ngx.ERR, "can't get plugins from datastore : " .. err)
return false
end
plugins = cjson.decode(plugins)
-- Call log() methods
logger:log(ngx.INFO, "calling log() methods of plugins ...")
for i, plugin in ipairs(plugins) do
local plugin_lua, err = helpers.new_plugin(plugin.id)
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
else
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
end
if plugin_lua ~= nil then
local ok, ret = helpers.call_plugin(plugin_lua, "log")
if ok == false then
logger:log(ngx.ERR, ret)
elseif ok == nil then
logger:log(ngx.INFO, ret)
else
if ret.ret then
logger:log(ngx.INFO, plugin.id .. ":log() call successful : " .. ret.msg)
else
-- Check if plugin has log method
if plugin_lua.log ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
logger:log(ngx.ERR, plugin.id .. ":log() call failed : " .. ret.msg)
local ok, ret = helpers.call_plugin(plugin_obj, "log")
if not ok then
logger:log(ngx.ERR, ret)
else
logger:log(ngx.INFO, plugin.id .. ":log() call successful : " .. ret.msg)
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin.id .. " because method log() is not defined")
end
end
end

View File

@ -2,57 +2,56 @@ set $dummy_set "";
set_by_lua_block $dummy_set {
local class = require "middleclass"
local logger = require "bunkerweb.logger"
local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local datastore = require "bunkerweb.datastore"
local cachestore = require "bunkerweb.cachestore"
local cdatastore = require "bunkerweb.datastore"
local cjson = require "cjson"
-- Don't process internal requests
logger:new("SET")
local logger = clogger:new("SET")
if ngx.req.is_internal() then
logger:log(ngx.INFO, "skipped access phase because request is internal")
logger:log(ngx.INFO, "skipped set phase because request is internal")
return true
end
-- Start set phase
datastore:new()
local datastore = cdatastore:new()
logger:log(ngx.INFO, "set phase started")
-- Update cachestore only once and before any other code
cachestore:new()
local ok, err = cachestore.cache:update()
if not ok then
logger:log(ngx.ERR, "can't update cachestore : " .. err)
end
-- Get plugins
local plugins, err = datastore:get("plugins")
if not plugins then
logger:log(ngx.ERR, "can't get plugins from datastore : " .. err)
return false
end
plugins = cjson.decode(plugins)
-- Call set() methods
logger:log(ngx.INFO, "calling set() methods of plugins ...")
for i, plugin in ipairs(plugins) do
local plugin_lua, err = helpers.new_plugin(plugin.id)
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
else
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
end
if plugin_lua ~= nil then
local ok, ret = helpers.call_plugin(plugin_lua, "set")
if ok == false then
logger:log(ngx.ERR, ret)
elseif ok == nil then
logger:log(ngx.INFO, ret)
else
if ret.ret then
logger:log(ngx.INFO, plugin.id .. ":set() call successful : " .. ret.msg)
else
-- Check if plugin has set method
if plugin_lua.set ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
if not ok then
logger:log(ngx.ERR, plugin_obj)
else
logger:log(ngx.ERR, plugin.id .. ":set() call failed : " .. ret.msg)
local ok, ret = helpers.call_plugin(plugin_obj, "set")
if not ok then
logger:log(ngx.ERR, ret)
else
logger:log(ngx.INFO, plugin.id .. ":set() call successful : " .. ret.msg)
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin.id .. " because method set() is not defined")
end
end
end

View File

@ -2,7 +2,6 @@ local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local datastore = require "bunkerweb.datastore"
local logger = require "bunkerweb.logger"
local cjson = require "cjson"
local captcha = require "antibot.captcha"
local base64 = require "base64"
@ -12,21 +11,17 @@ local http = require "resty.http"
local antibot = class("antibot", plugin)
function antibot:new()
-- Call parent new
local ok, err = plugin.new(self, "antibot")
if not ok then
return false, err
end
function antibot:initialize()
-- Call parent initialize
plugin.initialize(self, "antibot")
-- Check if init is needed
if ngx.get_phase() == "init" then
local init_needed, err = utils.has_not_variable("USE_ANTIBOT", "no")
if init_needed == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.init_needed = init_needed
end
return true, "success"
end
function antibot:init()
@ -41,7 +36,7 @@ function antibot:init()
templates[template] = f:read("*all")
f:close()
end
local ok, err = datastore:set("plugin_antibot_templates", cjson.encode(templates))
local ok, err = self.datastore:set("plugin_antibot_templates", cjson.encode(templates))
if not ok then
return self:ret(false, "can't save templates to datastore : " .. err)
end
@ -201,7 +196,7 @@ function antibot:display_challenge(challenge_uri)
end
-- Load HTML templates
local str_templates, err = datastore:get("plugin_antibot_templates")
local str_templates, err = self.datastore:get("plugin_antibot_templates")
if not str_templates then
return false, "can't get templates from datastore : " .. err
end
@ -364,4 +359,4 @@ function antibot:check_challenge()
return nil, "unknown", nil
end
return _M
return antibot

View File

@ -5,7 +5,6 @@
local _M = {}
local gd = require 'gd'
local logger = require "logger"
local mt = { __index = {} }

View File

@ -6,19 +6,15 @@ local clusterstore = require "bunkerweb.clusterstore"
local badbehavior = class("badbehavior", plugin)
function badbehavior:new()
-- Call parent new
local ok, err = plugin.new(self, "badbehavior")
if not ok then
return false, err
end
function badbehavior:initialize()
-- Call parent initialize
plugin.initialize(self, "badbehavior")
-- Check if redis is enabled
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
return true, "success"
end
function badbehavior:log()

View File

@ -8,71 +8,70 @@ local ipmatcher = require "resty.ipmatcher"
local blacklist = class("blacklist", plugin)
function blacklist:new()
-- Call parent new
local ok, err = plugin.new(self, "blacklist")
if not ok then
return false, err
end
function blacklist:initialize()
-- Call parent initialize
plugin.initialize(self, "blacklist")
-- Check if redis is enabled
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
-- Check if init is needed
if ngx.get_phase() == "init" then
local init_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
if init_needed == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.init_needed = init_needed
-- Decode lists
else
local lists, err = datastore:get("plugin_blacklist_lists")
local lists, err = self.datastore:get("plugin_blacklist_lists")
if not lists then
return false, err
self.logger:log(ngx.ERR, err)
else
self.lists = cjson.decode(lists)
end
self.lists = cjson.decode(lists)
end
-- Instantiate cachestore
cachestore:new(use_redis)
return true, "success"
self.cachestore = cachestore:new(self.use_redis)
end
function blacklist:init()
if self.init_needed then
-- Read blacklists
local blacklists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {},
["IGNORE_IP"] = {},
["IGNORE_RDNS"] = {},
["IGNORE_ASN"] = {},
["IGNORE_USER_AGENT"] = {},
["IGNORE_URI"] = {},
}
local i = 0
for kind, _ in pairs(blacklists) do
local f, err = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(blacklists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = datastore:set("plugin_blacklist_lists", cjson.encode(blacklists))
if not ok then
return self:ret(false, "can't store blacklist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
-- Check if init is needed
if not self.init_needed then
return self:ret(true, "init not needed")
end
-- Read blacklists
local blacklists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {},
["IGNORE_IP"] = {},
["IGNORE_RDNS"] = {},
["IGNORE_ASN"] = {},
["IGNORE_USER_AGENT"] = {},
["IGNORE_URI"] = {},
}
local i = 0
for kind, _ in pairs(blacklists) do
local f, err = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(blacklists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = self.datastore:set("plugin_blacklist_lists", cjson.encode(blacklists))
if not ok then
return self:ret(false, "can't store blacklist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
end
function blacklist:access()
@ -97,7 +96,7 @@ function blacklist:access()
}
for k, v in pairs(checks) do
local ok, cached = self:is_in_cache(v)
if not cached then
if not ok then
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
elseif cached and cached ~= "ok" then
return self:ret(true, k + " is in cached blacklist (info : " .. cached .. ")", utils.get_deny_status())
@ -115,9 +114,9 @@ function blacklist:access()
if not already_cached[k] then
local ok, blacklisted = self:is_blacklisted(k)
if ok == nil then
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. err)
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
else
local ok, err = self:add_to_cache(v, blacklisted)
local ok, err = self:add_to_cache(self:kind_to_ele(k), blacklisted)
if not ok then
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
end
@ -137,17 +136,27 @@ function blacklist:preread()
return self:access()
end
function blacklist:kind_to_ele(kind)
if kind == "IP" then
return "ip" .. ngx.var.remote_addr
elseif kind == "UA" then
return "ua" .. ngx.var.http_user_agent
elseif kind == "URI" then
return "uri" .. ngx.var.uri
end
end
function blacklist:is_in_cache(ele)
local ok, data = cachestore:get("plugin_blacklist_" .. ele)
if not ok then then
local ok, data = self.cachestore:get("plugin_blacklist_" .. ele)
if not ok then
return false, data
end
return true, data
end
function blacklist:add_to_cache(ele, value)
local ok, err = cachestore:set("plugin_blacklist_" .. ele, value)
if not ok then then
local ok, err = self.cachestore:set("plugin_blacklist_" .. ele, value)
if not ok then
return false, err
end
return true
@ -156,10 +165,11 @@ end
function blacklist:is_blacklisted(kind)
if kind == "IP" then
return self:is_blacklisted_ip()
elseif kind == "URI"
elseif kind == "URI" then
return self:is_blacklisted_uri()
elseif kind == "UA"
elseif kind == "UA" then
return self:is_blacklisted_ua()
end
return false, "unknown kind " .. kind
end

View File

@ -7,29 +7,25 @@ local http = require "resty.http"
local bunkernet = class("bunkernet", plugin)
function bunkernet:new()
-- Call parent new
local ok, err = plugin.new(self, "bunkernet")
if not ok then
return false, err
end
function bunkernet:initialize()
-- Call parent initialize
plugin.initialize(self, "bunkernet")
-- Check if init is needed
if ngx.get_phase() == "init" then
local init_needed, err = utils.has_variable("USE_BUNKERNET", "yes")
if init_needed == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.init_needed = true
self.init_needed = init_needed
-- Get BunkerNet ID
else
local id, err = datastore:get("plugin_bunkernet_id")
local id, err = self.datastore:get("plugin_bunkernet_id")
if not id then
self.bunkernet_id = nil
else
self.bunkernet_id = id
end
end
return true, "success"
end
function bunkernet:init()
@ -46,7 +42,7 @@ function bunkernet:init()
id = f:read("*all"):gsub("[\r\n]", "")
f:close()
-- Store ID in datastore
local ok, err = datastore:set("plugin_bunkernet_id", id)
local ok, err = self.datastore:set("plugin_bunkernet_id", id)
if not ok then
return self:ret(false, "can't save instance ID to the datastore : " .. err)
end
@ -71,7 +67,7 @@ function bunkernet:init()
return self:ret(false, "error while reading database : " .. err)
end
f:close()
local ok, err = datastore:set("plugin_bunkernet_db", cjson.encode(db))
local ok, err = self.datastore:set("plugin_bunkernet_db", cjson.encode(db))
if not ok then
return self:ret(false, "can't store bunkernet database into datastore : " .. err)
end

View File

@ -4,12 +4,9 @@ local utils = require "bunkerweb.utils"
local cors = class("cors", plugin)
function cors:new()
-- Call parent new
local ok, err = plugin.new(self, "cors")
if not ok then
return false, err
end
function cors:initialize()
-- Call parent initialize
plugin.initialize(self, "cors")
end
function cors:header()
@ -28,7 +25,7 @@ function cors:header()
}
for variable, header in pairs(cors_headers) do
local value = self.variables[variable]
elseif value ~= "" then
if value ~= "" then
ngx.header[header] = value
end
end
@ -36,7 +33,7 @@ function cors:header()
ngx.header["Content-Length"] = "0"
-- Send CORS policy with a 204 (no content) status
return return self:ret(true, "sent CORS policy", ngx.HTTP_NO_CONTENT)
return self:ret(true, "sent CORS policy")
end
return cors

View File

@ -5,19 +5,16 @@ local cachestore = require "bunkerweb.cachestore"
local country = class("country", plugin)
function country:new()
-- Call parent new
local ok, err = plugin.new(self, "country")
if not ok then
return false, err
end
function country:initialize()
-- Call parent initialize
plugin.initialize(self, "country")
-- Instantiate cachestore
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
cachestore:new(use_redis)
return true, "success"
self.use_redis = use_redis == "yes"
self.cachestore = cachestore:new(self.use_redis)
end
function country:access()
@ -31,8 +28,7 @@ function country:access()
if data.result == "ok" then
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in country cache (not blacklisted, country = " .. data.country .. ")")
end
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in country cache (blacklisted, country = " .. data.country .. ")", utils.get_deny_status()
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in country cache (blacklisted, country = " .. data.country .. ")", utils.get_deny_status())
end
-- Don't go further if IP is not global
@ -58,14 +54,14 @@ function country:access()
for wh_country in self.variables["WHITELIST_COUNTRY"]:gmatch("%S+") do
if wh_country == country then
local ok, err = self:add_to_cache(ngx.var.remote_addr, country, "ok")
if not then
if not ok then
return self:ret(false, "error while adding item to cache : " .. err)
end
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is whitelisted (country = " .. country .. ")")
end
end
local ok, err = self:add_to_cache(ngx.var.remote_addr, country, "ko")
if not then
if not ok then
return self:ret(false, "error while adding item to cache : " .. err)
end
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is not whitelisted (country = " .. country .. ")", utils.get_deny_status())
@ -97,16 +93,16 @@ function country:preread()
end
function country:is_in_cache(ip)
local ok, data = cachestore:get("plugin_country_cache_" .. ip)
if not ok then then
local ok, data = self.cachestore:get("plugin_country_cache_" .. ip)
if not ok then
return false, data
end
return true, cjson.decode(data)
end
function country:add_to_cache(ip, country, result)
local ok, err = cachestore:set("plugin_country_cache_" .. ip, cjson.encode({country = country, result = result}))
if not ok then then
local ok, err = self.cachestore:set("plugin_country_cache_" .. ip, cjson.encode({country = country, result = result}))
if not ok then
return false, err
end
return true

View File

@ -7,15 +7,16 @@ local resolver = require "resty.dns.resolver"
local dnsbl = class("dnsbl", plugin)
local dnsbl:new()
-- Call parent new
local ok, err = plugin.new(self, "dnsbl")
if not ok then
return false, err
end
function dnsbl:initialize()
-- Call parent initialize
plugin.initialize(self, "dnsbl")
-- Instantiate cachestore
cachestore:new(use_redis)
return true, "success"
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
self.cachestore = cachestore:new(self.use_redis)
end
function dnsbl:access()
@ -27,14 +28,14 @@ function dnsbl:access()
return self:ret(true, "dnsbl list is empty")
end
-- Check if IP is in cache
local cache, err = self:is_in_cache(ngx.var.remote_addr)
if not cache and err ~= "success" then
local ok, cached = self:is_in_cache(ngx.var.remote_addr)
if not ok then
return self:ret(false, "error while checking cache : " .. err)
elseif cache then
if cache == "ok" then
elseif cached then
if cached == "ok" then
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in DNSBL cache (not blacklisted)")
end
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in DNSBL cache (server = " .. cache .. ")", utils.get_deny_status())
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")", utils.get_deny_status())
end
-- Don't go further if IP is not global
local is_global, err = utils.ip_is_global(ngx.var.remote_addr)
@ -74,17 +75,17 @@ function dnsbl:preread()
return self:access()
end
function dnsl:is_in_cache(ip)
local ok, data = cachestore:get("plugin_dnsbl_" .. ip)
if not ok then then
function dnsbl:is_in_cache(ip)
local ok, data = self.cachestore:get("plugin_dnsbl_" .. ip)
if not ok then
return false, data
end
return true, data
end
function dnsbl:add_to_cache(ip, value)
local ok, err = cachestore:set("plugin_dnsbl_" .. ip, value)
if not ok then then
local ok, err = self.cachestore:set("plugin_dnsbl_" .. ip, value)
if not ok then
return false, err
end
return true

View File

@ -28,8 +28,8 @@ location = {{ page }} {
root /usr/share/bunkerweb/core/files;
content_by_lua_block {
local logger = require "bunkerweb.logger"
local errors = require "errors.errors"
errors:new()
local cerrors = require "errors.errors"
local errors = cerrors:new()
if ngx.status == 200 then
errors:render_template(tostring(405))
else

View File

@ -1,18 +1,14 @@
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local cachestore = require "bunkerweb.cachestore"
local cjson = require "cjson"
local template = require "resty.template"
local errors = class("errors", plugin)
function errors:new()
-- Call parent new
local ok, err = plugin.new(self, "errors")
if not ok then
return false, err
end
function errors:initialize()
-- Call parent initialize
plugin.initialize(self, "errors")
-- Default error texts
self.default_errors = {
["400"] = {
@ -64,7 +60,6 @@ function errors:new()
text = "The gateway has timed out."
}
}
return true, "success"
end
function errors:render_template(code)

View File

@ -1,71 +1,71 @@
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local cjson = require "cjson"
local ipmatcher = require "resty.ipmatcher"
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local cachestore = require "bunkerweb.cachestore"
local cjson = require "cjson"
local ipmatcher = require "resty.ipmatcher"
local greylist = class("dnsbl", plugin)
local greylist = class("greylist", plugin)
function greylist:new()
-- Call parent new
local ok, err = plugin.new(self, "greylist")
if not ok then
return false, err
end
function greylist:initialize()
-- Call parent initialize
plugin.initialize(self, "greylist")
-- Check if redis is enabled
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
-- Check if init is needed
if ngx.get_phase() == "init" then
local init_needed, err = utils.has_variable("USE_GREYLIST", "yes")
if init_needed == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.init_needed = init_needed
-- Decode lists
else
local lists, err = datastore:get("plugin_greylist_lists")
elseif self.variables["USE_GREYLIST"] == "yes" then
local lists, err = self.datastore:get("plugin_greylist_lists")
if not lists then
return false, err
self.logger:log(ngx.ERR, err)
else
self.lists = cjson.decode(lists)
end
self.lists = cjson.decode(lists)
end
-- Instantiate cachestore
cachestore:new(use_redis)
return true, "success"
self.cachestore = cachestore:new(self.use_redis)
end
function greylist:init()
if self.init_needed then
-- Read blacklists
local greylists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {},
}
local i = 0
for kind, _ in pairs(greylists) do
local f, err = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(greylists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = datastore:set("plugin_greylist_lists", cjson.encode(greylists))
if not ok then
return self:ret(false, "can't store greylist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " bad IP/network/rDNS/ASN/User-Agent/URI")
-- Check if init is needed
if not self.init_needed then
return self:ret(true, "init not needed")
end
-- Read blacklists
local greylists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {},
}
local i = 0
for kind, _ in pairs(greylists) do
local f, err = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(greylists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = self.datastore:set("plugin_greylist_lists", cjson.encode(greylists))
if not ok then
return self:ret(false, "can't store greylist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " bad IP/network/rDNS/ASN/User-Agent/URI")
end
function greylist:access()
@ -110,7 +110,7 @@ function greylist:access()
if greylisted == nil then
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is greylisted : " .. err)
else
local ok, err = self:add_to_cache(v, greylisted or "ok")
local ok, err = self:add_to_cache(self:kind_to_ele(k), greylisted or "ok")
if not ok then
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
end
@ -129,13 +129,24 @@ function greylist:preread()
return self:access()
end
function greylist:kind_to_ele(kind)
if kind == "IP" then
return "ip" .. ngx.var.remote_addr
elseif kind == "UA" then
return "ua" .. ngx.var.http_user_agent
elseif kind == "URI" then
return "uri" .. ngx.var.uri
end
end
function greylist:is_greylisted(kind)
if kind == "IP" then
return self:is_greylisted_ip()
elseif kind == "URI"
elseif kind == "URI" then
return self:is_greylisted_uri()
elseif kind == "UA"
elseif kind == "UA" then
return self:is_greylisted_ua()
end
return false, "unknown kind " .. kind
end
@ -214,16 +225,16 @@ function greylist:is_greylisted_ua()
end
function greylist:is_in_cache(ele)
local ok, data = cachestore:get("plugin_greylist_" .. ele)
if not ok then then
local ok, data = self.cachestore:get("plugin_greylist_" .. ele)
if not ok then
return false, data
end
return true, data
end
function greylist:add_to_cache(ele, value)
local ok, err = cachestore:set("plugin_greylist_" .. ele, value)
if not ok then then
local ok, err = self.cachestore:set("plugin_greylist_" .. ele, value)
if not ok then
return false, err
end
return true

View File

@ -5,12 +5,9 @@ local cjson = require "cjson"
local letsencrypt = class("letsencrypt", plugin)
function letsencrypt:new()
-- Call parent new
local ok, err = plugin.new(self, "letsencrypt")
if not ok then
return false, err
end
function letsencrypt:initialize()
-- Call parent initialize
plugin.initialize(self, "letsencrypt")
end
function letsencrypt:access()

View File

@ -7,16 +7,13 @@ local cjson = require "cjson"
local limit = class("limit", plugin)
function limit:new()
-- Call parent new
local ok, err = plugin.new(self, "limit")
if not ok then
return false, err
end
function limit:initialize()
-- Call parent initialize
plugin.initialize(self, "limit")
-- Check if redis is enabled
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
-- Load rules if needed
@ -24,9 +21,10 @@ function limit:new()
if self.variables["USE_LIMIT_REQ"] == "yes" then
-- Get all rules from datastore
local limited = false
local all_rules, err = datastore:get("plugin_limit_rules")
local all_rules, err = self.datastore:get("plugin_limit_rules")
if not all_rules then
return false, err
self.logger:log(ngx.ERR, err)
return
end
all_rules = cjson.decode(all_rules)
self.rules = {}
@ -44,7 +42,6 @@ function limit:new()
end
end
end
return true, "success"
end
function limit:init()
@ -77,7 +74,7 @@ function limit:init()
end
end
end
local ok, err = datastore:set("plugin_limit_rules", cjson.encode(data))
local ok, err = self.datastore:set("plugin_limit_rules", cjson.encode(data))
if not ok then
return self:ret(false, err)
end
@ -119,11 +116,10 @@ function limit:access()
end
-- Limit reached
if limited then
return return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")", ngx.HTTP_TOO_MANY_REQUESTS)
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")", ngx.HTTP_TOO_MANY_REQUESTS)
end
-- Limit not reached
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is not limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")")
end
return self:ret(true, "client IP " .. ngx.var.remote_addr .. " is not limited for URL " .. ngx.var.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")")
end
function limit:limit_req(rate_max, rate_time)
@ -136,7 +132,7 @@ function limit:limit_req(rate_max, rate_time)
else
timestamps = redis_timestamps
-- Save the new timestamps
local ok, err = datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
local ok, err = self.datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
if not ok then
return nil, "can't update timestamps : " .. err
end
@ -158,7 +154,7 @@ end
function limit:limit_req_local(rate_max, rate_time)
-- Get timestamps
local timestamps, err = datastore:get("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri)
local timestamps, err = self.datastore:get("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri)
if not timestamps and err ~= "not found" then
return nil, err
elseif err == "not found" then
@ -169,7 +165,7 @@ function limit:limit_req_local(rate_max, rate_time)
local updated, new_timestamps, delay = self:limit_req_timestamps(rate_max, rate_time, timestamps)
-- Save new timestamps if needed
if updated then
local ok, err = datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
local ok, err = self.datastore:set("plugin_limit_cache_" .. ngx.var.server_name .. ngx.var.remote_addr .. ngx.var.uri, cjson.encode(timestamps), delay)
if not ok then
return nil, err
end

View File

@ -6,13 +6,9 @@ local clusterstore = require "bunkerweb.clusterstore"
local redis = class("redis", plugin)
function redis:new()
-- Call parent new
local ok, err = plugin.new(self, "redis")
if not ok then
return false, err
end
return true, "success"
function redis:initialize()
-- Call parent initialize
plugin.initialize(self, "redis")
end
function redis:init()

View File

@ -5,19 +5,16 @@ local cachestore = require "bunkerweb.cachestore"
local reversescan = class("reversescan", plugin)
function reversescan:new()
-- Call parent new
local ok, err = plugin.new(self, "reversescan")
if not ok then
return false, err
end
function reversescan:initialize()
-- Call parent initialize
plugin.initialize(self, "reversescan")
-- Instantiate cachestore
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
cachestore:new(use_redis)
return true, "success"
self.use_redis = use_redis == "yes"
self.cachestore = cachestore:new(self.use_redis)
end
function reversescan:access()
@ -64,16 +61,16 @@ function reversescan:scan(ip, port, timeout)
end
function reversescan:is_in_cache(ip_port)
local ok, data = cachestore:get("plugin_reversescan_cache_" .. ip_port)
if not ok then then
local ok, data = self.cachestore:get("plugin_reversescan_cache_" .. ip_port)
if not ok then
return false, data
end
return true, data
end
function reversescan:add_to_cache(ip_port, value)
local ok, err = cachestore:set("plugin_reversescan_cache_" .. ip_port, value)
if not ok then then
local ok, err = self.cachestore:set("plugin_reversescan_cache_" .. ip_port, value)
if not ok then
return false, err
end
return true

View File

@ -1,22 +1,18 @@
local _M = {}
_M.__index = _M
local utils = require "utils"
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local session = require "resty.session"
function _M.new()
local self = setmetatable({}, _M)
return self, nil
local sessions = class("sessions", plugin)
function sessions:initialize()
-- Call parent initialize
plugin.initialize(self, "sessions")
end
function _M:init()
-- Get vars
local vars = {
["SESSIONS_SECRET"] = "",
["SESSIONS_NAME"] = "",
["SESSIONS_IDLING_TIMEOUT"] = "",
["SESSIONS_ROLLING_TIMEOUT"] = "",
["SESSIONS_ABSOLUTE_TIMEOUT"] = "",
function sessions:init()
-- Get redis vars
local redis_vars = {
["USE_REDIS"] = "",
["REDIS_HOST"] = "",
["REDIS_PORT"] = "",
@ -25,46 +21,46 @@ function _M:init()
["REDIS_KEEPALIVE_IDLE"] = "",
["REDIS_KEEPALIVE_POOL"] = ""
}
for k, v in pairs(vars) do
for k, v in pairs(redis_vars) do
local var, err = utils.get_variable(k, false)
if var == nil then
return false, "can't get " .. k .. " variable : " .. err
return self:ret(false, "can't get " .. k .. " variable : " .. err)
end
end
-- Init configuration
local config = {
secret = vars["SESSIONS_SECRET"],
cookie_name = vars["SESSIONS_NAME"],
idling_timeout = tonumber(vars["SESSIONS_IDLING_TIMEOUT"]),
rolling_timeout = tonumber(vars["SESSIONS_ROLLING_TIMEOUT"]),
absolute_timeout = tonumber(vars["SESSIONS_ABSOLUTE_TIMEOUT"])
secret = self.variables["SESSIONS_SECRET"],
cookie_name = self.variables["SESSIONS_NAME"],
idling_timeout = tonumber(self.variables["SESSIONS_IDLING_TIMEOUT"]),
rolling_timeout = tonumber(self.variables["SESSIONS_ROLLING_TIMEOUT"]),
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"])
}
if vars["SESSIONS_SECRET"] == "random" then
if self.variables["SESSIONS_SECRET"] == "random" then
config.secret = utils.rand(16)
end
if vars["SESSIONS_NAME"] == "random" then
if self.variables["SESSIONS_NAME"] == "random" then
config.cookie_name = utils.rand(16)
end
if vars["USE_REDIS"] == "no" then
if redis_vars["USE_REDIS"] ~= "yes" then
config.storage = "cookie"
else
config.storage = "redis"
config.redis = {
prefix = "sessions_",
connect_timeout = tonumber(vars["REDIS_TIMEOUT"]),
send_timeout = tonumber(vars["REDIS_TIMEOUT"]),
read_timeout = tonumber(vars["REDIS_TIMEOUT"]),
keepalive_timeout = tonumber(vars["REDIS_KEEPALIVE_IDLE"]),
connect_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
pool = "bw",
pool_size = tonumber(vars["REDIS_KEEPALIVE_POOL"]),
ssl = vars["REDIS_SSL"] == "yes",
host = vars["REDIS_HOST"],
port = tonumber(vars["REDIS_HOST"]),
database = tonumber(vars["REDIS_DATABASE"])
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
ssl = redis_vars["REDIS_SSL"] == "yes",
host = redis_vars["REDIS_HOST"],
port = tonumber(redis_vars["REDIS_HOST"]),
database = tonumber(redis_vars["REDIS_DATABASE"])
}
end
session.init(config)
return true, "sessions init successful"
return self:ret(true, "sessions init successful")
end
return _M
return sessions

View File

@ -9,66 +9,65 @@ local env = require "resty.env"
local whitelist = class("whitelist", plugin)
function whitelist:new()
-- Call parent new
local ok, err = plugin.new(self, "whitelist")
if not ok then
return false, err
end
function whitelist:initialize()
-- Call parent initialize
plugin.initialize(self, "whitelist")
-- Check if redis is enabled
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return false, err
self.logger:log(ngx.ERR, err)
end
self.use_redis = use_redis == "yes"
-- Check if init is needed
if ngx.get_phase() == "init" then
local init_needed, err = utils.has_variable("USE_WHITELIST", "yes")
if init_needed == nil then
return false, err
self.logger:log(ngx.ERR, err)
end
self.init_needed = init_needed
-- Decode lists
else
local lists, err = datastore:get("plugin_whitelist_lists")
local lists, err = self.datastore:get("plugin_whitelist_lists")
if not lists then
return false, err
self.logger:log(ngx.ERR, err)
else
self.lists = cjson.decode(lists)
end
self.lists = cjson.decode(lists)
end
-- Instantiate cachestore
cachestore:new(use_redis)
return true, "success"
self.cachestore = cachestore:new(self.use_redis)
end
function whitelist:init()
if self.init_needed then
-- Read whitelists
local whitelists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {}
}
local i = 0
for kind, _ in pairs(whitelists) do
local f, err = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(whitelists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = datastore:set("plugin_whitelist_lists", cjson.encode(whitelists))
if not ok then
return self:ret(false, "can't store whitelist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
-- Check if init is needed
if not self.init_needed then
return self:ret(true, "init not needed")
end
-- Read whitelists
local whitelists = {
["IP"] = {},
["RDNS"] = {},
["ASN"] = {},
["USER_AGENT"] = {},
["URI"] = {}
}
local i = 0
for kind, _ in pairs(whitelists) do
local f, err = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
if f then
for line in f:lines() do
table.insert(whitelists[kind], line)
i = i + 1
end
f:close()
end
end
-- Load them into datastore
local ok, err = self.datastore:set("plugin_whitelist_lists", cjson.encode(whitelists))
if not ok then
return self:ret(false, "can't store whitelist list into datastore : " .. err)
end
return self:ret(true, "successfully loaded " .. tostring(i) .. " IP/network/rDNS/ASN/User-Agent/URI")
end
function whitelist:set()
@ -112,7 +111,7 @@ function whitelist:access()
if ok == nil then
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. err)
else
local ok, err = self:add_to_cache(v, whitelisted)
local ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
if not ok then
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
end
@ -132,6 +131,16 @@ function whitelist:preread()
return self:access()
end
function whitelist:kind_to_ele(kind)
if kind == "IP" then
return "ip" .. ngx.var.remote_addr
elseif kind == "UA" then
return "ua" .. ngx.var.http_user_agent
elseif kind == "URI" then
return "uri" .. ngx.var.uri
end
end
function whitelist:check_cache()
-- Check the caches
local checks = {
@ -168,16 +177,16 @@ function whitelist:check_cache()
end
function whitelist:is_in_cache(ele)
local ok, data = cachestore:get("plugin_whitelist_" .. ele)
if not ok then then
local ok, data = self.cachestore:get("plugin_whitelist_" .. ele)
if not ok then
return false, data
end
return true, data
end
function whitelist:add_to_cache(ele, value)
local ok, err = cachestore:set("plugin_whitelist_" .. ele, value)
if not ok then then
local ok, err = self.cachestore:set("plugin_whitelist_" .. ele, value)
if not ok then
return false, err
end
return true
@ -186,10 +195,11 @@ end
function whitelist:is_whitelisted(kind)
if kind == "IP" then
return self:is_whitelisted_ip()
elseif kind == "URI"
elseif kind == "URI" then
return self:is_whitelisted_uri()
elseif kind == "UA"
elseif kind == "UA" then
return self:is_whitelisted_ua()
end
return false, "unknown kind " .. kind
end

View File

@ -254,14 +254,30 @@ git_secure_clone "https://github.com/bungle/lua-resty-template.git" "c08c6bc9e27
echo " Downloading lua-resty-lock"
git_secure_clone "https://github.com/openresty/lua-resty-lock.git" "9dc550e56b6f3b1a2f1a31bb270a91813b5b6861"
# lua-pack v2.0.0
echo " Downloading lua-pack"
dopatch="no"
if [ ! -d "deps/src/lua-pack" ] ; then
dopatch="yes"
fi
git_secure_clone "https://github.com/Kong/lua-pack.git" "495bf30606b9744140258df349862981e3ee7820"
if [ "$dopatch" = "yes" ] ; then
do_and_check_cmd cp deps/misc/lua-pack.Makefile deps/src/lua-pack/Makefile
fi
# lua-resty-openssl v0.8.21
echo " Downloading lua-resty-openssl"
git_secure_clone "https://github.com/fffonion/lua-resty-openssl.git" "15bc59b97feb5acf25fbdd9426cf73870cf7c838"
# ModSecurity v3.0.9
echo " Downloading ModSecurity"
dopatch="no"
if [ ! -d "deps/src/ModSecurity" ] ; then
dopatch="yes"
dopatch="yes"
fi
git_secure_clone "https://github.com/SpiderLabs/ModSecurity.git" "205dac0e8c675182f96b5c2fb06be7d1cf7af2b2"
if [ "$dopatch" = "yes" ] ; then
do_and_check_cmd patch deps/src/ModSecurity/configure.ac deps/misc/modsecurity.patch
do_and_check_cmd patch deps/src/ModSecurity/configure.ac deps/misc/modsecurity.patch
fi
# libinjection v3.10.0+

View File

@ -135,6 +135,14 @@ do_and_check_cmd cp -r /tmp/bunkerweb/deps/src/lua-resty-template/lib/resty/* /u
echo " Installing lua-resty-lock"
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-lock" do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
# Installing lua-pack
echo " Installing lua-pack"
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-pack" do_and_check_cmd make INST_LIBDIR=/usr/share/bunkerweb/deps/lib/lua LUA_LIBDIR=-L/usr/share/bunkerweb/deps/lib LUA_INCDIR=-I/usr/share/bunkerweb/deps/include install
# Installing lua-resty-openssl
echo " Installing lua-resty-openssl"
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-openssl" do_and_check_cmd make LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
# Compile dynamic modules
echo " Compiling and installing dynamic modules"
CONFARGS="$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p')"

View File

@ -0,0 +1,24 @@
LUA ?= lua5.1
LUA_LIBDIR ?= $(shell pkg-config $(LUA) --libs)
LUA_INCDIR ?= $(shell pkg-config $(LUA) --cflags)
LIBFLAG ?= -shared
CFLAGS ?= -std=c99 -O2 -Wall
.PHONY: all clean install
all: lua_pack.so
lua_pack.so: lua_pack.o
$(CC) $(LIBFLAG) $(LUA_LIBDIR) $< -o $@
%.o: %.c
$(CC) -c $(CFLAGS) -fPIC $(LUA_INCDIR) $< -o $@
install: lua_pack.so
cp lua_pack.so $(INST_LIBDIR)
clean:
rm -f *.so *.o *.rock
# eof

View File

@ -0,0 +1,19 @@
--- /dev/null
+++ /dev/null
@@ -322,12 +322,12 @@
# Decide if we want to build the tests or not.
-buildTestUtilities=false
-if test "x$YAJL_FOUND" = "x1"; then
+# buildTestUtilities=false
+# if test "x$YAJL_FOUND" = "x1"; then
# Regression tests will not be able to run without the logging support.
# But we still have the unit tests.
# if test "$debugLogs" = "true"; then
- buildTestUtilities=true
+# buildTestUtilities=true
# fi
-fi
+# fi

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Mashape, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,24 @@
LUA ?= lua5.1
LUA_LIBDIR ?= $(shell pkg-config $(LUA) --libs)
LUA_INCDIR ?= $(shell pkg-config $(LUA) --cflags)
LIBFLAG ?= -shared
CFLAGS ?= -std=c99 -O2 -Wall
.PHONY: all clean install
all: lua_pack.so
lua_pack.so: lua_pack.o
$(CC) $(LIBFLAG) $(LUA_LIBDIR) $< -o $@
%.o: %.c
$(CC) -c $(CFLAGS) -fPIC $(LUA_INCDIR) $< -o $@
install: lua_pack.so
cp lua_pack.so $(INST_LIBDIR)
clean:
rm -f *.so *.o *.rock
# eof

View File

@ -0,0 +1,35 @@
This library is extended from `lpack` library on Luarocks to support Hexdecimal data too.
This is a simple Lua library for packing and unpacking binary data.
The library provides two functions: `pack` and `unpack`.
`pack` is called as follows: `pack(F,x1,x2,...)`, where `F` is a string describing
how the values `x1`, `x2`, ... are to be interpreted and formatted. Each letter
in the format string `F` consumes one of the given values. Only values of type
number or string are accepted. `pack` returns a (binary) string containing the
values packed as described in `F`. The letter codes understood by pack are listed
in lpack.c (they are inspired by Perl's codes but are not the same). Numbers
following letter codes in `F` indicate repetitions.
`unpack` is called as follows: `unpack(s,F,[init])`, where `s` is a (binary) string
containing data packed as if by `pack`, `F` is a format string describing what is
to be read from `s`, and the optional `init` marks where in `s` to begin reading the
values. `unpack` returns one value per letter in `F` until `F` or `s` is exhausted
(the letters codes are the same as for `pack`, except that numbers following ``A'`
are interpreted as the number of characters to read into the string, not as
repetitions).
The first value returned by `unpack` is the next unread position in `s`, which can
be used as the init position in a subsequent call to `unpack`. This allows you to
unpack values in a loop or in several steps. If the position returned by `unpack`
is beyond the end of `s`, then `s` has been exhausted; any calls to `unpack` starting
beyond the end of `s` will always return `nil` values.
-------------------------------------------------------------------------------
```
pack library:
pack(f,...) unpack(s,f,[init])
```
-------------------------------------------------------------------------------

View File

@ -0,0 +1,25 @@
package="lua_pack"
version="2.0.0-0"
source = {
url = "git://github.com/mashape/lua-pack",
tag = "2.0.0"
}
description = {
summary = "This is a simple Lua library for packing and unpacking binary data",
detailed = [[
This is a simple Lua library for packing and unpacking binary data.
]],
homepage = "https://github.com/mashape/lua-pack",
license = "MIT"
}
dependencies = {
"lua >= 5.1, < 5.3"
}
build = {
type = "builtin",
modules = {
["lua_pack"] = {
sources = { "lua_pack.c" },
}
}
}

View File

@ -0,0 +1,396 @@
/***
This is a simple Lua library for packing and unpacking binary data.
@license MIT
@module lua_pack
*/
#define OP_ZSTRING 'z' /* zero-terminated string */
#define OP_BSTRING 'p' /* string preceded by length byte */
#define OP_WSTRING 'P' /* string preceded by length word */
#define OP_SSTRING 'a' /* string preceded by length size_t */
#define OP_STRING 'A' /* string */
#define OP_FLOAT 'f' /* float */
#define OP_DOUBLE 'd' /* double */
#define OP_NUMBER 'n' /* Lua number */
#define OP_CHAR 'c' /* char */
#define OP_BYTE 'C' /* byte = unsigned char */
#define OP_SHORT 's' /* short */
#define OP_USHORT 'S' /* unsigned short */
#define OP_INT 'i' /* int */
#define OP_UINT 'I' /* unsigned int */
#define OP_LONG 'l' /* long */
#define OP_ULONG 'L' /* unsigned long */
#define OP_LITTLEENDIAN '<' /* little endian */
#define OP_BIGENDIAN '>' /* big endian */
#define OP_NATIVE '=' /* native endian */
#define OP_BINMSB 'B'
#define OP_HEX 'X'
#define OP_NULL 'x'
#include <ctype.h>
#include <string.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static void badcode(lua_State *L, int c)
{
char s[]="bad code `?'";
s[sizeof(s)-3]=c;
luaL_argerror(L,1,s);
}
static int doendian(int c)
{
int x=1;
int e=*(char*)&x;
if (c==OP_LITTLEENDIAN) return !e;
if (c==OP_BIGENDIAN) return e;
if (c==OP_NATIVE) return 0;
return 0;
}
static void doswap(int swap, void *p, size_t n)
{
if (swap)
{
char *a=p;
int i,j;
for (i=0, j=n-1, n=n/2; n--; i++, j--)
{
char t=a[i]; a[i]=a[j]; a[j]=t;
}
}
}
#define UNPACKNUMBER(OP,T) \
case OP: \
{ \
T a; \
int m=sizeof(a); \
if (i+m>len) goto done; \
memcpy(&a,s+i,m); \
i+=m; \
doswap(swap,&a,m); \
lua_pushnumber(L,(lua_Number)a); \
++n; \
break; \
}
#define UNPACKSTRING(OP,T) \
case OP: \
{ \
T l; \
int m=sizeof(l); \
if (i+m>len) goto done; \
memcpy(&l,s+i,m); \
doswap(swap,&l,m); \
if (i+m+l>len) goto done; \
i+=m; \
lua_pushlstring(L,s+i,l); \
i+=l; \
++n; \
break; \
}
#define HEXDIGITS(DIG) \
"0123456789ABCDEF"[DIG]
static int l_unpack(lua_State *L) /** unpack(s,f,[init]) */
{
size_t len;
const char *s=luaL_checklstring(L,1,&len);
const char *f=luaL_checkstring(L,2);
int i=luaL_optnumber(L,3,1)-1;
int n=0;
int swap=0;
lua_pushnil(L);
while (*f)
{
int c=*f++;
int N=1;
if (isdigit(*f))
{
N=0;
while (isdigit(*f)) N=10*N+(*f++)-'0';
if (N==0 && c==OP_STRING) { lua_pushliteral(L,""); ++n; }
}
while (N--) switch (c)
{
case OP_LITTLEENDIAN:
case OP_BIGENDIAN:
case OP_NATIVE:
{
swap=doendian(c);
N=0;
break;
}
case OP_STRING:
{
++N;
if (i+N>len) goto done;
lua_pushlstring(L,s+i,N);
i+=N;
++n;
N=0;
break;
}
case OP_ZSTRING:
{
size_t l;
if (i>=len) goto done;
l=strlen(s+i);
lua_pushlstring(L,s+i,l);
i+=l+1;
++n;
break;
}
UNPACKSTRING(OP_BSTRING, unsigned char)
UNPACKSTRING(OP_WSTRING, unsigned short)
UNPACKSTRING(OP_SSTRING, size_t)
UNPACKNUMBER(OP_NUMBER, lua_Number)
UNPACKNUMBER(OP_DOUBLE, double)
UNPACKNUMBER(OP_FLOAT, float)
UNPACKNUMBER(OP_CHAR, char)
UNPACKNUMBER(OP_BYTE, unsigned char)
UNPACKNUMBER(OP_SHORT, short)
UNPACKNUMBER(OP_USHORT, unsigned short)
UNPACKNUMBER(OP_INT, int)
UNPACKNUMBER(OP_UINT, unsigned int)
UNPACKNUMBER(OP_LONG, long)
UNPACKNUMBER(OP_ULONG, unsigned long)
case OP_BINMSB:
{
luaL_Buffer buf;
luaL_buffinit(L,&buf);
unsigned char sbyte = 0x80;
N++;
if (i+N > len) goto done;
unsigned int ii;
for (ii = i; ii < i+N; ii++) {
sbyte = 0x80;
int ij;
for (ij = 0; ij < 8; ij++) {
if (s[ii] & sbyte) {
luaL_addlstring(&buf, "1", 1);
} else {
luaL_addlstring(&buf, "0", 1);
}
sbyte = sbyte >> 1;
}
}
luaL_pushresult(&buf);
n++;
i += N;
N = 0;
break;
}
case OP_HEX:
{
luaL_Buffer buf;
char hdigit = '0';
int val = 0;
luaL_buffinit(L,&buf);
N++;
if (i+N > len) goto done;
unsigned int ii;
for (ii = i; ii < i+N; ii++) {
val = s[ii] & 0xF0;
val = val >> 4;
hdigit = HEXDIGITS(val);
luaL_addlstring(&buf, &hdigit, 1);
val = s[ii] & 0x0F;
hdigit = HEXDIGITS(val);
luaL_addlstring(&buf, &hdigit, 1);
}
luaL_pushresult(&buf);
n++;
i += N;
N = 0;
break;
}
case OP_NULL:
{
i+= N + 1;
N = 0;
break;
}
case ' ': case ',':
break;
default:
badcode(L,c);
break;
}
}
done:
lua_pushnumber(L,i+1);
lua_replace(L,-n-2);
return n+1;
}
#define PACKNUMBER(OP,T) \
case OP: \
{ \
T a=(T)luaL_checknumber(L,i++); \
doswap(swap,&a,sizeof(a)); \
luaL_addlstring(&b,(void*)&a,sizeof(a)); \
break; \
}
#define PACKSTRING(OP,T) \
case OP: \
{ \
size_t l; \
const char *a=luaL_checklstring(L,i++,&l); \
T ll=(T)l; \
doswap(swap,&ll,sizeof(ll)); \
luaL_addlstring(&b,(void*)&ll,sizeof(ll)); \
luaL_addlstring(&b,a,l); \
break; \
}
static int l_pack(lua_State *L) /** pack(f,...) */
{
int i=2;
const char *f=luaL_checkstring(L,1);
int swap=0;
luaL_Buffer b;
luaL_buffinit(L,&b);
while (*f)
{
int c=*f++;
int N=1;
if (isdigit(*f))
{
N=0;
while (isdigit(*f)) N=10*N+(*f++)-'0';
}
while (N--) switch (c)
{
case OP_LITTLEENDIAN:
case OP_BIGENDIAN:
case OP_NATIVE:
{
swap=doendian(c);
N=0;
break;
}
case OP_STRING:
case OP_ZSTRING:
{
size_t l;
const char *a=luaL_checklstring(L,i++,&l);
luaL_addlstring(&b,a,l+(c==OP_ZSTRING));
break;
}
PACKSTRING(OP_BSTRING, unsigned char)
PACKSTRING(OP_WSTRING, unsigned short)
PACKSTRING(OP_SSTRING, size_t)
PACKNUMBER(OP_NUMBER, lua_Number)
PACKNUMBER(OP_DOUBLE, double)
PACKNUMBER(OP_FLOAT, float)
PACKNUMBER(OP_CHAR, char)
PACKNUMBER(OP_BYTE, unsigned char)
PACKNUMBER(OP_SHORT, short)
PACKNUMBER(OP_USHORT, unsigned short)
PACKNUMBER(OP_INT, int)
PACKNUMBER(OP_UINT, unsigned int)
PACKNUMBER(OP_LONG, long)
PACKNUMBER(OP_ULONG, unsigned long)
case OP_BINMSB:
{
unsigned char sbyte = 0;
size_t l;
unsigned int ii = 0, ia = 0;
const char *a = luaL_checklstring(L, i++, &l);
for (ia = 0; ia < l; ia+= 8) {
sbyte = 0;
for (ii = 0; ii+ia < l && ii < 8; ii++) {
sbyte = sbyte << 1;
if (a[ii+ia] != '0') {
sbyte++;
}
}
for (; ii < 8; ii++) {
sbyte = sbyte << 1;
}
luaL_addlstring(&b, (char *) &sbyte, 1);
}
break;
}
case OP_NULL:
{
char nullbyte = 0;
luaL_addlstring(&b, &nullbyte, 1);
break;
}
case OP_HEX:
{ // doing digit parsing the lpack way
unsigned char sbyte = 0;
size_t l;
unsigned int ii = 0;
int odd = 0;
const char *a = luaL_checklstring(L, i++, &l);
for (ii = 0; ii < l; ii++) {
if (isxdigit((int) (unsigned char) a[ii])) {
if (isdigit((int) (unsigned char) a[ii])) {
sbyte += a[ii] - '0';
odd++;
} else if (a[ii] >= 'A' && a[ii] <= 'F') {
sbyte += a[ii] - 'A' + 10;
odd++;
} else if (a[ii] >= 'a' && a[ii] <= 'f') {
sbyte += a[ii] - 'a' + 10;
odd++;
}
if (odd == 1) {
sbyte = sbyte << 4;
} else if (odd == 2) {
luaL_addlstring(&b, (char *) &sbyte, 1);
sbyte = 0;
odd = 0;
}
} else if (isspace(a[ii])) {
/* ignore */
} else {
/* err ... ignore too*/
}
}
if (odd == 1) {
luaL_addlstring(&b, (char *) &sbyte, 1);
}
break;
}
case ' ': case ',':
break;
default:
badcode(L,c);
break;
}
}
luaL_pushresult(&b);
return 1;
}
static const luaL_Reg R[] =
{
{"pack", l_pack},
{"unpack", l_unpack},
{NULL, NULL}
};
int luaopen_lua_pack(lua_State *L)
{
lua_newtable(L);
lua_pushcfunction(L, &l_pack);
lua_setfield(L, -2, "pack");
lua_pushcfunction(L, &l_unpack);
lua_setfield(L, -2, "unpack");
return 1;
}

View File

@ -0,0 +1,27 @@
local pack = require"lua_pack"
local bpack = pack.pack
local bunpack = pack.unpack
function hex(s)
s=string.gsub(s,"(.)",function (x) return string.format("%02X",string.byte(x)) end)
return s
end
a=bpack("AC8","\027Lua",5*16+1,0,1,4,4,4,8,0)
print(hex(a),string.len(a))
b=string.dump(hex)
b=string.sub(b,1,string.len(a))
print(a==b,string.len(b))
print(bunpack(b,"CA3C8"))
i=314159265 f="<I>I=I"
a=bpack(f,i,i,i)
print(hex(a))
print(bunpack(a,f))
i=3.14159265 f="<d>d=d"
a=bpack(f,i,i,i)
print(hex(a))
print(bunpack(a,f))

View File

@ -1,6 +1,6 @@
---
sudo: required
dist: bionic
dist: focal
branches:
only:
@ -16,7 +16,6 @@ compiler:
addons:
apt:
packages:
- cpanminus
- axel
- luarocks
- daemonize
@ -56,7 +55,7 @@ before_install:
- luacheck --globals coroutine -q .
- '! grep -n -P ''(?<=.{80}).+'' --color `find . -name ''*.lua''` || (echo "ERROR: Found Lua source lines exceeding 80 columns." > /dev/stderr; exit 1)'
- '! grep -n -P ''\t+'' --color `find . -name ''*.lua''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)'
- sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
- cpanm --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
install:
- if [ ! -d download-cache ]; then mkdir download-cache; fi

View File

@ -19,22 +19,22 @@ local FREE_LIST_REF = 0
if subsystem == 'http' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 10023
or ngx.config.ngx_lua_version ~= 10024
then
error("ngx_http_lua_module 0.10.23 required")
error("ngx_http_lua_module 0.10.24 required")
end
elseif subsystem == 'stream' then
if not ngx.config
or not ngx.config.ngx_lua_version
or ngx.config.ngx_lua_version ~= 12
or ngx.config.ngx_lua_version ~= 13
then
error("ngx_stream_lua_module 0.0.12 required")
error("ngx_stream_lua_module 0.0.13 required")
end
else
error("ngx_http_lua_module 0.10.23 or "
.. "ngx_stream_lua_module 0.0.12 required")
error("ngx_http_lua_module 0.10.24 or "
.. "ngx_stream_lua_module 0.0.13 required")
end
@ -141,7 +141,7 @@ local c_buf_type = ffi.typeof("char[?]")
local _M = new_tab(0, 18)
_M.version = "0.1.25"
_M.version = "0.1.26"
_M.new_tab = new_tab
_M.clear_tab = clear_tab

View File

@ -20,6 +20,7 @@ local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local setmetatable = setmetatable
local lower = string.lower
local find = string.find
local rawget = rawget
local ngx = ngx
local get_request = base.get_request
@ -114,7 +115,12 @@ local truncated = ffi.new("int[1]")
local req_headers_mt = {
__index = function (tb, key)
return rawget(tb, (str_replace_char(lower(key), '_', '-')))
key = lower(key)
local value = rawget(tb, key)
if value == nil and find(key, '_', 1, true) then
value = rawget(tb, (str_replace_char(key, '_', '-')))
end
return value
end
}

View File

@ -2,13 +2,16 @@
local ffi = require "ffi"
local jit = require "jit"
local base = require "resty.core.base"
local ffi_cast = ffi.cast
local C = ffi.C
local ffi_new = ffi.new
local new_tab = base.new_tab
local subsystem = ngx.config.subsystem
local get_string_buf = base.get_string_buf
local get_size_ptr = base.get_size_ptr
local ngx_lua_ffi_worker_id
@ -16,6 +19,8 @@ local ngx_lua_ffi_worker_pid
local ngx_lua_ffi_worker_pids
local ngx_lua_ffi_worker_count
local ngx_lua_ffi_worker_exiting
local ffi_intp_type = ffi.typeof("int *")
local ffi_int_size = ffi.sizeof("int")
ngx.worker = new_tab(0, 4)
@ -25,31 +30,42 @@ if subsystem == "http" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_id(void);
int ngx_http_lua_ffi_worker_pid(void);
int ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len);
int ngx_http_lua_ffi_worker_count(void);
int ngx_http_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_http_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_http_lua_ffi_worker_pid
ngx_lua_ffi_worker_pids = C.ngx_http_lua_ffi_worker_pids
ngx_lua_ffi_worker_count = C.ngx_http_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_http_lua_ffi_worker_exiting
if jit.os ~= "Windows" then
ffi.cdef[[
int ngx_http_lua_ffi_worker_pids(int *pids, size_t *pids_len);
]]
ngx_lua_ffi_worker_pids = C.ngx_http_lua_ffi_worker_pids
end
elseif subsystem == "stream" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_id(void);
int ngx_stream_lua_ffi_worker_pid(void);
int ngx_stream_lua_ffi_worker_pids(int *pids, size_t *pids_len);
int ngx_stream_lua_ffi_worker_count(void);
int ngx_stream_lua_ffi_worker_exiting(void);
]]
ngx_lua_ffi_worker_id = C.ngx_stream_lua_ffi_worker_id
ngx_lua_ffi_worker_pid = C.ngx_stream_lua_ffi_worker_pid
ngx_lua_ffi_worker_pids = C.ngx_stream_lua_ffi_worker_pids
ngx_lua_ffi_worker_count = C.ngx_stream_lua_ffi_worker_count
ngx_lua_ffi_worker_exiting = C.ngx_stream_lua_ffi_worker_exiting
if jit.os ~= "Windows" then
ffi.cdef[[
int ngx_stream_lua_ffi_worker_pids(int *pids, size_t *pids_len);
]]
ngx_lua_ffi_worker_pids = C.ngx_stream_lua_ffi_worker_pids
end
end
@ -62,23 +78,33 @@ function ngx.worker.pid()
return ngx_lua_ffi_worker_pid()
end
local size_ptr = ffi_new("size_t[1]")
local pids_ptr = ffi_new("int[1024]") -- using NGX_MAX_PROCESSES
function ngx.worker.pids()
if ngx.get_phase() == "init" or ngx.get_phase() == "init_worker" then
return nil, "API disabled in the current context"
end
local res = ngx_lua_ffi_worker_pids(pids_ptr, size_ptr)
local pids = {}
if res == 0 then
for i = 1, tonumber(size_ptr[0]) do
pids[i] = pids_ptr[i-1]
if jit.os ~= "Windows" then
function ngx.worker.pids()
if ngx.get_phase() == "init" or ngx.get_phase() == "init_worker" then
return nil, "API disabled in the current context"
end
local pids = {}
local size_ptr = get_size_ptr()
-- the old and the new workers coexist during reloading
local worker_cnt = ngx.worker.count() * 4
if worker_cnt == 0 then
return pids
end
size_ptr[0] = worker_cnt
local pids_ptr = get_string_buf(worker_cnt * ffi_int_size)
local intp_buf = ffi_cast(ffi_intp_type, pids_ptr)
local res = ngx_lua_ffi_worker_pids(intp_buf, size_ptr)
if res == 0 then
for i = 1, tonumber(size_ptr[0]) do
pids[i] = intp_buf[i-1]
end
end
return pids
end
return pids
end
function ngx.worker.id()

View File

@ -140,6 +140,11 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
=== TEST 4: ngx.req.get_headers (metatable)
--- http_config eval
"
$::HttpConfig
underscores_in_headers on;
"
--- config
location = /t {
set $foo hello;
@ -159,6 +164,10 @@ qr/\[TRACE\s+\d+ content_by_lua\(nginx\.conf:\d+\):4 loop\]/
for _, k in ipairs(keys) do
ngx.say(k, ": ", headers[k])
end
ngx.say("X_Bar_Header: ", headers["X_Bar_Header"])
ngx.say("x_Bar_Header: ", headers["x_Bar_Header"])
ngx.say("x_bar_header: ", headers["x_bar_header"])
}
}
--- request
@ -169,9 +178,14 @@ baz: baz
connection: close
foo-bar: foo
host: localhost
x_bar_header: bar
X_Bar_Header: bar
x_Bar_Header: bar
x_bar_header: bar
--- more_headers
Foo-Bar: foo
Baz: baz
X_Bar_Header: bar
--- wait: 0.2
--- error_log eval
qr/\[TRACE\s+\d+ .*? -> \d+\]/

View File

@ -0,0 +1,42 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ replace .Scope "*" "\\*" -1 }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ lower .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ replace .Scope "*" "\\*" -1 }}:** {{ end }}{{ .Subject }} [{{ .Hash.Short }}]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }})
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

View File

@ -0,0 +1,29 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/fffonion/lua-resty-openssl
options:
sort: "semver"
commits:
filters:
Type:
- feat
- fix
- perf
- refactor
commit_groups:
title_maps:
feat: Features
fix: Bug Fixes
perf: Performance Improvements
refactor: Code Refactoring
header:
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s\\/]*)\\))?\\s(.*)$"
pattern_maps:
- Type
- Scope
- Subject
notes:
keywords:
- BREAKING CHANGE

View File

@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.lua]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

View File

@ -0,0 +1,32 @@
name: Lint
on:
push:
paths:
- lib/**.lua
pull_request:
paths:
- lib/**.lua
jobs:
tests:
name: Lint
runs-on: ubuntu-22.04
steps:
- name: Checkout source code
uses: actions/checkout@v2
- uses: Jayrgo/luacheck-action@v1
name: luacheck
with:
# List of files, directories and rockspecs to check.
# Default: .
files: 'lib'
# Path to configuration file.
# Default: .luacheckrc
config: '.luacheckrc'
# Arguments passed to luacheck.
# Default: -q
args: '-q'

View File

@ -0,0 +1,299 @@
name: Tests
on:
pull_request:
paths-ignore:
- '*.md'
push:
branches:
- master
- release/*
paths-ignore:
- '*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
tests:
name: Tests
runs-on: ubuntu-22.04
strategy:
matrix:
include:
# TODO: arm64
# latest and one version older for valgrind
- nginx: "1.19.9"
openssl: "1.0.2u"
valgrind: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
- nginx: "1.19.9"
openssl: "1.1.1s"
valgrind: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
- nginx: "1.19.9"
openssl: "3.0.8"
valgrind: "valgrind"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "1.0.2u"
valgrind: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
openssl: "1.1.1s"
valgrind: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
openssl: "3.0.8"
valgrind: "valgrind"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "3.1.0-beta1"
valgrind: "valgrind"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
nginx_cc_opts: "-Wno-error"
# latest version with EOL 1.1.0
- nginx: "1.21.4"
openssl: "1.1.0l"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
# version that kong uses, for fips
- nginx: "1.21.4"
openssl: "1.0.2u"
fips2: "2.0.16"
openssl_opts: "fips --with-fipsdir=/home/runner/work/cache/ssl/fips"
valgrind: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
boringssl: "ae223d6138807a13006342edfeef32e813246b39" # fips-20190808
valgrind: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
boringssl: "853ca1ea1168dff08011e5d42d94609cc0ca2e27" # fips-20210429, not active yet
valgrind: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
env:
JOBS: 3
SH: bash
NGX_BUILD_JOBS: 3
BASE_PATH: /home/runner/work/cache
LUAJIT_PREFIX: /home/runner/work/cache/luajit21
LUAJIT_LIB: /home/runner/work/cache/luajit21/lib
LUAJIT_INC: /home/runner/work/cache/luajit21/include/luajit-2.1
LUA_INCLUDE_DIR: /home/runner/work/cache/luajit21/include/luajit-2.1
OPENSSL_PREFIX: /home/runner/work/cache/ssl
# lib64 since openssl 3.0
OPENSSL_LIB: /home/runner/work/cache/ssl/lib64
OPENSSL_INC: /home/runner/work/cache/ssl/include
TEST_NGINX_SLEEP: 0.005
TEST_NGINX_RANDOMIZE: 1
LUACHECK_VER: 0.21.1
CC: gcc
NGX_BUILD_CC: gcc
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Setup cache
uses: actions/cache@v3
with:
path: |
/home/runner/work/cache
key: ${{ runner.os }}-${{ hashFiles('**/tests.yml') }}-nginx-${{ matrix.nginx }}-openssl-${{ matrix.openssl }}-${{ matrix.fips2 }}-boringssl-${{ matrix.boringssl }}
- name: Setup tools
run: |
sudo apt-get update
sudo apt-get install -qq -y cpanminus axel ca-certificates valgrind haveged
mkdir -p $OPENSSL_PREFIX $LUAJIT_PREFIX
# perl cache
pushd /home/runner/work/cache
if [ ! -e perl ]; then sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1); cp -r /usr/local/share/perl/ .; else sudo cp -r perl /usr/local/share; fi
# build tools at parent directory of cache
cd ..
git clone https://github.com/openresty/openresty.git ./openresty
git clone https://github.com/openresty/nginx-devel-utils.git
git clone https://github.com/simpl/ngx_devel_kit.git ./ndk-nginx-module
git clone https://github.com/openresty/lua-nginx-module.git ./lua-nginx-module -b ${{ matrix.lua_nginx_module }}
git clone https://github.com/openresty/no-pool-nginx.git ./no-pool-nginx
git clone https://github.com/fffonion/lua-resty-openssl-aux-module ./lua-resty-openssl-aux-module
# lua libraries at parent directory of current repository
popd
git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core -b ${{ matrix.lua_resty_core }}
git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
git clone https://github.com/jkeys089/lua-resty-hmac ../lua-resty-hmac && pushd ../lua-resty-hmac && git checkout 79a4929 && popd
git clone https://github.com/openresty/lua-resty-string ../lua-resty-string
- name: Build OpenSSL
if: matrix.boringssl == ''
run: |
mkdir -p $OPENSSL_PREFIX
# fips doesn't seem to support to build parallelly
if [ "X${{ matrix.fips2 }}" != "X" ]; then wget https://www.openssl.org/source/old/fips/openssl-fips-${{ matrix.fips2 }}.tar.gz -qO - | tar zxf - ; pushd openssl-fips-${{ matrix.fips2 }}/; FIPSDIR=$OPENSSL_PREFIX/fips ./config; make; make install; popd; fi
if [ "X$OPENSSL_HASH" != "X" ]; then wget https://github.com/openssl/openssl/archive/$OPENSSL_HASH.tar.gz -qO - | tar zxf ; pushd openssl-$OPENSSL_HASH/; fi
if [ "X$OPENSSL_HASH" = "X" ] ; then wget https://www.openssl.org/source/openssl-${{ matrix.openssl }}.tar.gz -qO - | tar zxf -; pushd openssl-${{ matrix.openssl }}/; fi
if [ ! -e $OPENSSL_PREFIX/include ]; then ./config shared -d --prefix=$OPENSSL_PREFIX -DPURIFY ${{ matrix.openssl_opts }} > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/include ]; then make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/include ]; then sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1); fi
if [ -e $OPENSSL_LIB/libcrypto.so.3 ] && [ ! -e $OPENSSL_LIB/ossl-modules/fips.so ]; then mkdir -p $OPENSSL_PREFIX/ssl; sudo make PATH=$PATH install_fips > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/lib64 ]; then sudo cp -r $OPENSSL_PREFIX/lib $OPENSSL_PREFIX/lib64; fi
mkdir -p $OPENSSL_PREFIX/certs/ && sudo cp -r /etc/ssl/certs/* $OPENSSL_PREFIX/certs/
- name: Build BoringSSL
if: matrix.boringssl != ''
run: |
mkdir -p $OPENSSL_PREFIX
if [ ! -e $OPENSSL_PREFIX/include ]; then
# libtinfo5 is a dependency of clang7 on ubuntu20.04
sudo apt-get install -qq -y cmake libtinfo5 unzip libunwind-dev libgcc-9-dev libstdc++-9-dev
wget https://releases.llvm.org/7.0.1/clang+llvm-7.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz -qO - |tar Jxf -
export HOME="$PWD"
printf "set(CMAKE_C_COMPILER \"clang\")\nset(CMAKE_CXX_COMPILER \"clang++\")\n" > ${HOME}/toolchain
export PATH="$PWD/clang+llvm-7.0.1-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH"
clang --version
wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz -qO - |tar zxf -
export GOPATH="$PWD/gopath"
export GOROOT="$PWD/go"
export PATH="$GOPATH/bin:$GOROOT/bin:$PATH"
go version
wget https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip -q
unzip -o ninja-linux.zip
export PATH="$PWD:$PATH"
ninja --version
wget https://commondatastorage.googleapis.com/chromium-boringssl-fips/boringssl-${{ matrix.boringssl }}.tar.xz -qO - | tar Jxf -; pushd boringssl
if [ "${{ matrix.boringssl }}" == "ae223d6138807a13006342edfeef32e813246b39" ]; then
patch -p1 < ../t/fixtures/boringssl_fips.patch
fi
rm -rf build; mkdir build; pushd build
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 .. > build.log 2>&1 || (cat build.log && exit 1)
ninja > build.log 2>&1 || (cat build.log && exit 1)
./tool/bssl isfips
popd; rm -rf $OPENSSL_INC; cp -r include $OPENSSL_INC
mkdir -p $OPENSSL_LIB; cp -r build/*/*.so $OPENSSL_LIB
fi
mkdir -p $OPENSSL_PREFIX/certs/ && sudo cp -r /etc/ssl/certs/* $OPENSSL_PREFIX/certs/
- name: Build LuaJIT
env:
LUAJIT_CC_OPTS: ${{ matrix.luajit_cc_opts }}
run: |
if [ "X${{ matrix.valgrind }}" != "X" ]; then LUAJIT_CC_OPTS="$LUAJIT_CC_OPTS -DLUAJIT_NUMMODE=2 -DLUAJIT_${{ matrix.valgrind }} -DLUAJIT_USE_SYSMALLOC -O0"; fi
export
cd $LUAJIT_PREFIX
if [ ! -e luajit2 ]; then git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git; fi
cd luajit2
make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS="-DLUA_USE_APICHECK -DLUA_USE_ASSERT -DLUAJIT_ENABLE_LUA52COMPAT ${{ matrix.luajit_cc_opts }}" > build.log 2>&1 || (cat build.log && exit 1)
make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
- name: Build lua-cjson
run: |
if [ ! -e lua-cjson ]; then git clone https://github.com/openresty/lua-cjson.git ./lua-cjson; fi
pushd ./lua-cjson && make && sudo PATH=$PATH make install && popd
- name: Build Nginx
env:
NGINX_CC_OPTS: ${{ matrix.nginx_cc_opts }}
run: |
if [ "X${{ matrix.valgrind }}" != "X" ]; then NGINX_CC_OPTS="$NGINX_CC_OPTS -O0"; fi
export PATH=$BASE_PATH/work/nginx/sbin:$BASE_PATH/../nginx-devel-utils:$PATH
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export NGX_LUA_LOC=$BASE_PATH/../lua-nginx-module
export NGX_STREAM_LUA_LOC=$BASE_PATH/../stream-lua-nginx-module
export
cd $BASE_PATH
if [ ! -e work ]; then ngx-build ${{ matrix.nginx }} --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --add-module=../lua-resty-openssl-aux-module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC $NGINX_CC_OPTS" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --with-debug > build.log 2>&1 || (cat build.log && exit 1); fi
nginx -V
ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- name: Run Test
run: |
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
TEST_NGINX_TIMEOUT=20 prove -j$JOBS -r t/ 2>&1
echo "Nginx SSL plain FFI"
export CI_SKIP_NGINX_C=1
TEST_NGINX_TIMEOUT=10 prove -j$JOBS t/openssl/ssl/ 2>&1
- name: Run Valgrind
if: matrix.valgrind != ''
run: |
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export TEST_NGINX_VALGRIND='--num-callers=100 -q --tool=memcheck --leak-check=full --show-possibly-lost=no --gen-suppressions=all --suppressions=valgrind.suppress --track-origins=yes' TEST_NGINX_TIMEOUT=60 TEST_NGINX_SLEEP=1
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
stdbuf -o 0 -e 0 prove -j$JOBS -r t/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
echo "Nginx SSL plain FFI"
export CI_SKIP_NGINX_C=1
stdbuf -o 0 -e 0 prove -j$JOBS t/openssl/ssl/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
- name: Run FIPS Test
run: |
# openssl 3.0
if [ -e $OPENSSL_LIB/libcrypto.so.3 ]; then
echo "FIPS for OpenSSL 3.0"
cp t/fixtures/openssl_fips.cnf $OPENSSL_PREFIX/openssl-fips.cnf
pushd openssl-${{ matrix.openssl }}/;
# LD_LIBRARY_PATH=$OPENSSL_LIB $OPENSSL_PREFIX/bin/openssl fipsinstall -out $OPENSSL_PREFIX/fipsmodule.cnf -module $OPENSSL_LIB/ossl-modules/fips.so
# don't activate by default
sed -i "/activate = 1/d" $OPENSSL_PREFIX/ssl/fipsmodule.cnf
cat $OPENSSL_PREFIX/ssl/fipsmodule.cnf >> $OPENSSL_PREFIX/openssl-fips.cnf
export OPENSSL_CONF=$OPENSSL_PREFIX/openssl-fips.cnf
popd
export TEST_NGINX_FIPS=1
fi
# openssl 1.0.2 with fips module
if [ "X${{ matrix.fips2 }}" != "X" ]; then
echo "FIPS for OpenSSL 1.0.2"
export TEST_NGINX_FIPS=1
fi
# BoringSSL
if [ "X${{ matrix.boringssl }}" != "X" ]; then
echo "FIPS for BoringSSL ${{ matrix.boringssl }}"
export TEST_NGINX_FIPS=1
fi
if [ "X$TEST_NGINX_FIPS" != "X" ]; then
echo "Running FIPS tests"
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
TEST_NGINX_FIPS=1 TEST_NGINX_TIMEOUT=10 prove -j$JOBS -r t/ 2>&1
TEST_NGINX_TIMEOUT=20 prove -j$JOBS -r t/ 2>&1
fi

View File

@ -0,0 +1,3 @@
t/servroot
__pycache__
.idea/

View File

@ -0,0 +1,15 @@
std = "ngx_lua"
unused_args = false
redefined = false
max_line_length = false
not_globals = {
"string.len",
"table.getn",
}
ignore = {
"6.", -- ignore whitespace warnings
}

View File

@ -0,0 +1,546 @@
<a name="unreleased"></a>
## [Unreleased]
<a name="0.8.21"></a>
## [0.8.21] - 2023-03-24
### features
- **x509.store:** extend verify to support setting flags ([#104](https://github.com/fffonion/lua-resty-openssl/issues/104)) [fa45b6c](https://github.com/fffonion/lua-resty-openssl/commit/fa45b6ce197dee7e2a55601bd4833f415c6cbaa2)
<a name="0.8.20"></a>
## [0.8.20] - 2023-03-10
### bug fixes
- **pkey:** use group bits instead of ECDSA_sig to get parameter size in ECDSA signature ([#102](https://github.com/fffonion/lua-resty-openssl/issues/102)) [f12cbfc](https://github.com/fffonion/lua-resty-openssl/commit/f12cbfc123490c666e2cbd7bec90948910a02336)
<a name="0.8.19"></a>
## [0.8.19] - 2023-03-10
### bug fixes
- **pkey:** fix signature length for secp521r1 ecdsa signature length ([#100](https://github.com/fffonion/lua-resty-openssl/issues/100)) [b7303d4](https://github.com/fffonion/lua-resty-openssl/commit/b7303d49cf738fe134f3e5efbf6157c96ff85237)
<a name="0.8.18"></a>
## [0.8.18] - 2023-03-04
### features
- **bn:** to_binary supports left padding of zeros [d59cac9](https://github.com/fffonion/lua-resty-openssl/commit/d59cac9d7e019e1bcdeaa6714f61294c354cf141)
- **pkey:** allow to convert to and from binary format of ecdsa signature [9a20323](https://github.com/fffonion/lua-resty-openssl/commit/9a203233a23dd08d8f7eeaaff0599921d752a2e2)
<a name="0.8.17"></a>
## [0.8.17] - 2023-01-20
### bug fixes
- **\*:** support OpenSSL 3.1 [dc932f3](https://github.com/fffonion/lua-resty-openssl/commit/dc932f394e5c2b94129b406480897535ec561355)
- **pkey:** allow one shot sign/verify in BoringSSL [32e5df3](https://github.com/fffonion/lua-resty-openssl/commit/32e5df37ac1aaa060c2c1f9b599bd194247d5ecb)
<a name="0.8.16"></a>
## [0.8.16] - 2022-12-20
### features
- **pkey:** load PKCS[#1](https://github.com/fffonion/lua-resty-openssl/issues/1) PEM encoded RSAPublicKey and RSAPrivateKey [3246ec0](https://github.com/fffonion/lua-resty-openssl/commit/3246ec0e51252bfa2812d49f9c6385dcaf0af10b)
<a name="0.8.15"></a>
## [0.8.15] - 2022-10-28
### bug fixes
- **pkey:** check private key existence before doing sign ([#83](https://github.com/fffonion/lua-resty-openssl/issues/83)) [eefcd2a](https://github.com/fffonion/lua-resty-openssl/commit/eefcd2a80b240f44be0bdadd1c2ccc28612004c0)
<a name="0.8.14"></a>
## [0.8.14] - 2022-10-21
### bug fixes
- **x509.crl:** fix metamethods when revoked is empty ([#79](https://github.com/fffonion/lua-resty-openssl/issues/79)) [e65adc7](https://github.com/fffonion/lua-resty-openssl/commit/e65adc7f132628c97e4db69cb5c4b13ff9cf0abf)
<a name="0.8.13"></a>
## [0.8.13] - 2022-10-14
### bug fixes
- **x509.\*:** fix set_extension will fail when a extension with same NID is not exist yet ([#75](https://github.com/fffonion/lua-resty-openssl/issues/75)) [b2f57b8](https://github.com/fffonion/lua-resty-openssl/commit/b2f57b860509a371ab1df71bbbc9e176e5a4d004)
### features
- **x509.altname:** support set and get IP addresses ([#74](https://github.com/fffonion/lua-resty-openssl/issues/74)) [363c80d](https://github.com/fffonion/lua-resty-openssl/commit/363c80d1f2c7ba29dce268e213a9a16c9eae2953)
- **x509.store:** add set_flags ([#77](https://github.com/fffonion/lua-resty-openssl/issues/77)) [8f3f16a](https://github.com/fffonion/lua-resty-openssl/commit/8f3f16a2b6d6c0f680c781f20a9e84a631da9aa5)
<a name="0.8.11"></a>
## [0.8.11] - 2022-10-12
### performance improvements
- **\*:** reuse cdata to improve performance [fc9cecd](https://github.com/fffonion/lua-resty-openssl/commit/fc9cecd785fc0193290cc3398d1ebbe7ae66fe15)
<a name="0.8.10"></a>
## [0.8.10] - 2022-06-24
### features
- **x509:** add get_signature_digest_name [d54b5d6](https://github.com/fffonion/lua-resty-openssl/commit/d54b5d61bc14813121f4a6bda2e1d7eab215094a)
<a name="0.8.9"></a>
## [0.8.9] - 2022-06-23
### bug fixes
- **aux/nginx:** add nginx 1.21.4 and ngx_lua 0.10.21 to support matrix [028da56](https://github.com/fffonion/lua-resty-openssl/commit/028da56d7de606d4b4b323fb3686ad4d93f69c7d)
<a name="0.8.8"></a>
## [0.8.8] - 2022-04-14
### bug fixes
- **ctx:** use global ctx where request is unavailable [e3590cf](https://github.com/fffonion/lua-resty-openssl/commit/e3590cfcbeb6f0d5f110c3c4e1b6cdc63b88e001)
- **x509.extension:** correct X509V3_CTX size for OpenSSL 3.0 [0946c59](https://github.com/fffonion/lua-resty-openssl/commit/0946c5937fa9fa4bb41a70267a67fcc87307b6a6)
### features
- **x509.extension:** add X509V3_set_issuer_pkey in OpenSSL 3.0 [dbd3f74](https://github.com/fffonion/lua-resty-openssl/commit/dbd3f7418a665ae797e6ffc71ba1d7f0660c95f0)
- **x509.store:** add set_purpose and verify_method parameter [b7500fe](https://github.com/fffonion/lua-resty-openssl/commit/b7500fe7212c26070363afeab4a8acfe44c3cfc8)
<a name="0.8.7"></a>
## [0.8.7] - 2022-03-18
### features
- **x509.crl:** add functions to find and inspect revoked list in CRL [37c1661](https://github.com/fffonion/lua-resty-openssl/commit/37c1661fbebebad3b804f602f631e4ba65b80e07)
<a name="0.8.6"></a>
## [0.8.6] - 2022-03-16
### bug fixes
- **obj:** clean up stale error occured from OBJ_txt2* [219a2f0](https://github.com/fffonion/lua-resty-openssl/commit/219a2f0cace8480800394d6e88b188138f2650a1)
- **pkey:** clear_error in passphrase type mismatch [8577422](https://github.com/fffonion/lua-resty-openssl/commit/857742273629d4e801a2d862644213fe5fdbf02a)
- **x509.\*:** move clear_error to last when loading [369eea1](https://github.com/fffonion/lua-resty-openssl/commit/369eea1e4a1a185055296e07f272a3e470442916)
### features
- **openssl:** add function to list SSL ciphers [9861af1](https://github.com/fffonion/lua-resty-openssl/commit/9861af1a074f74f529e341049ada29cbf7d57a48)
- **ssl:** refine various handshake controlling functions [30bf41e](https://github.com/fffonion/lua-resty-openssl/commit/30bf41e958775f60afff1976fe731978c816dd25)
<a name="0.8.5"></a>
## [0.8.5] - 2022-02-02
### bug fixes
- **\*:** correct size type in cipher, hmac and rand in BoringSSL [54ce5f0](https://github.com/fffonion/lua-resty-openssl/commit/54ce5f0dd1861f2af15eacea154c805a237c03d8)
- **bn:** use BN_check_prime in OpenSSL 3.0 [8c107e3](https://github.com/fffonion/lua-resty-openssl/commit/8c107e3dcf2006d6c453234278ab0a45109042d6)
- **kdf:** correct FFI definition for BoringSSL [30ba7cf](https://github.com/fffonion/lua-resty-openssl/commit/30ba7cf9d90d8bc611cbccdca83e69c308739b60)
- **stack:** correct indices to use size_t in BoringSSL [526ecb8](https://github.com/fffonion/lua-resty-openssl/commit/526ecb89c81b0e477b749a2424231329e468ce02)
### features
- **\*:** add more modules for OSSL_LIB_CTX support [35f4bcb](https://github.com/fffonion/lua-resty-openssl/commit/35f4bcb796bc2fbe4ab066b8f78047bf30118986)
<a name="0.8.4"></a>
## [0.8.4] - 2021-12-20
### bug fixes
- **x509.\*:** use SHA256 as default sign digest in BoringSSL [355681a](https://github.com/fffonion/lua-resty-openssl/commit/355681a33d88d85de0faae3e8eb6685e0e3b9f34)
### features
- **pkey:** add pkey:get_default_digest_type [0572e57](https://github.com/fffonion/lua-resty-openssl/commit/0572e57e0ab418f2dd749dbc5042b0c680e346a7)
<a name="0.8.3"></a>
## [0.8.3] - 2021-12-16
### bug fixes
- **hmac:** include evp.md headers [125ea05](https://github.com/fffonion/lua-resty-openssl/commit/125ea059da6b1effef7a187c434ebd6022dc3b82)
<a name="0.8.2"></a>
## [0.8.2] - 2021-11-22
### bug fixes
- **jwk:** fix typo of secp521r1 [81d2a64](https://github.com/fffonion/lua-resty-openssl/commit/81d2a646bde7a66ab87e127eace0d40aa714be58)
<a name="0.8.1"></a>
## [0.8.1] - 2021-11-05
### bug fixes
- **ssl_ctx:** fix typo when getting SSL_CTX from request [7b9e90f](https://github.com/fffonion/lua-resty-openssl/commit/7b9e90faef8337c759c281172be8c1f599be704d)
### features
- **ctx:** add ctx module to provide OSSL_LIB_CTX context [65750bf](https://github.com/fffonion/lua-resty-openssl/commit/65750bfd800b2eebeb9bf653a03518f3ad235fba)
<a name="0.8.0"></a>
## [0.8.0] - 2021-10-29
### bug fixes
- **\*:** move EVP_* definition into seperate files [e0c3d61](https://github.com/fffonion/lua-resty-openssl/commit/e0c3d6178e8b0baab5c53d331dedf8ffb1b1b0c7)
- **auxiliary/nginx:** set off_t to 64bit per nginx config ([#32](https://github.com/fffonion/lua-resty-openssl/issues/32)) [8c209fa](https://github.com/fffonion/lua-resty-openssl/commit/8c209fabbd4ba2f1d6f3a267059c758b4697a433)
- **pkey:** allow sign/verify without md_alg for EdDSA on BoringSSL [ab83fd4](https://github.com/fffonion/lua-resty-openssl/commit/ab83fd4fc053f496699d5dcc77dbb551e2389e77)
- **x509:** compatibility for BoringSSL 1.1.0 (fips-20190808) [84244af](https://github.com/fffonion/lua-resty-openssl/commit/84244af7d91e3421dfccdf1940beb70adbd66adb)
### features
- **evp:** add geneirc function to get and set params [c724e1d](https://github.com/fffonion/lua-resty-openssl/commit/c724e1d41010fab7fb112ca3674eef1aab0b06be)
- **kdf:** add new API with EVP_KDF interfaces [2336ae3](https://github.com/fffonion/lua-resty-openssl/commit/2336ae3b9a7a05473e251a10523a7357afb6f2f2)
- **mac:** add EVP_MAC [0625be9](https://github.com/fffonion/lua-resty-openssl/commit/0625be92e0eaf6a9ee61b3499690d6079aaf933d)
- **openssl:** add function list mac and kdf algorithms and set properties for EVP algorithm fetches [0ed8316](https://github.com/fffonion/lua-resty-openssl/commit/0ed83167dbb1b7d8171bcb59cb749187220572e2)
- **openssl:** support FIPS in OpenSSL 3.0 [beb3ad3](https://github.com/fffonion/lua-resty-openssl/commit/beb3ad3ec8f162aeb11d3f89ea8211c2f3e38c1e)
- **param:** add new function to use OSSL_PARAM [5ffbbcc](https://github.com/fffonion/lua-resty-openssl/commit/5ffbbcce386d98127c84b7f24bb019cff76c05e3)
- **provider:** cipher, digest, kdf, pkey and x509 can now fetch by provider and has new get_provider_name function [52938ca](https://github.com/fffonion/lua-resty-openssl/commit/52938ca5b66f48186815975d685227836bd92cef)
<a name="0.7.5"></a>
## [0.7.5] - 2021-09-18
### bug fixes
- **\*:** rename some EVP_ API to use get in openssl3.0 [8fbdb39](https://github.com/fffonion/lua-resty-openssl/commit/8fbdb396d0a4988a24ff2e0404c1866a416d9cff)
- **aux/nginx:** add 1.19.9 [eb73691](https://github.com/fffonion/lua-resty-openssl/commit/eb73691c058c9d55a1b57405f889f5bc3ecd0420)
<a name="0.7.4"></a>
## [0.7.4] - 2021-08-02
### bug fixes
- **extension:** fallback to ASN1_STRING_print in extension:text where X509V3_EXT_print is not available [f0268f5](https://github.com/fffonion/lua-resty-openssl/commit/f0268f55b124eb4ff65b472899e241af850f9d35)
<a name="0.7.3"></a>
## [0.7.3] - 2021-06-29
### bug fixes
- **pkey:** only pass in passphrase/passphrase_cb to PEM_* functions [6a56494](https://github.com/fffonion/lua-resty-openssl/commit/6a564949e08a6dbe87a44d82e694d862b77c8b68)
- **pkey:** avoid callbacks overflow when setting passphrase_cb [e8aec4e](https://github.com/fffonion/lua-resty-openssl/commit/e8aec4e3ceb4419e373938f9ad4b592efa43acfc)
### features
- **pkey:** allow to specify digest type and padding scheme in sign/verify [ff982ba](https://github.com/fffonion/lua-resty-openssl/commit/ff982ba374ab543c440ccab597d71cdbf4560cdb)
<a name="0.7.2"></a>
## [0.7.2] - 2021-03-25
### bug fixes
- **\*:** redefine callback functions to a style FFI will not overflow [f91202c](https://github.com/fffonion/lua-resty-openssl/commit/f91202c57b826d935d831ec452d2b90fc33277fa)
<a name="0.7.1"></a>
## [0.7.1] - 2021-03-18
### bug fixes
- **altname:** return unsupported as value in not implemented types [ef5e1ed](https://github.com/fffonion/lua-resty-openssl/commit/ef5e1eda9eaea1fd4c8d7d65e438275fed10cdc6)
- **auxiliary/nginx:** typo in error message [4bd22d8](https://github.com/fffonion/lua-resty-openssl/commit/4bd22d81419ed160af1dcea16f42fd284f8f2ad5)
<a name="0.7.0"></a>
## [0.7.0] - 2021-02-19
### bug fixes
- **csr:** count extension count in openssl 3.0 [5af0f4b](https://github.com/fffonion/lua-resty-openssl/commit/5af0f4b02edd0fb8c461a1e08b04eb4eb781f744)
- **csr:** BREAKING: remove csr:set_subject_alt function [513fd8a](https://github.com/fffonion/lua-resty-openssl/commit/513fd8ac61b6f7775465eabc5a3d6a454ccebc54)
- **openssl:** include crypto header in openssl.lua [ef54bf7](https://github.com/fffonion/lua-resty-openssl/commit/ef54bf72710f2613ac6d6e5e8ebb712fa7135939)
- **openssl:** BREAKING: not load sub modules by default [a402f05](https://github.com/fffonion/lua-resty-openssl/commit/a402f05f3ea4b85589c1de6b4347cdfc4c397ea7)
### features
- **\*:** support BoringSSL [9c4e5dc](https://github.com/fffonion/lua-resty-openssl/commit/9c4e5dccefb7fa2e08c489e2922ea05e043e28f2)
- **bn:** add generate_prime [2cc77a4](https://github.com/fffonion/lua-resty-openssl/commit/2cc77a4513dad2f4d684535d1230484e8e91bfbd)
- **openssl:** add function to list supported cipher and digest algorithms [5bdc2a4](https://github.com/fffonion/lua-resty-openssl/commit/5bdc2a406c974f471331636de670915df9386f82)
- **openssl:** add function to get and set fips mode [f6de183](https://github.com/fffonion/lua-resty-openssl/commit/f6de183b19e57616ded39e73518acd198c730056)
<a name="0.6.11"></a>
## [0.6.11] - 2021-01-21
### bug fixes
- **aux/nginx:** only show warning message when function is being called [9964a6d](https://github.com/fffonion/lua-resty-openssl/commit/9964a6d29aded1c0d06c1a8700ee313e08506c2f)
- **openssl:** not load ssl modules by default [390ad79](https://github.com/fffonion/lua-resty-openssl/commit/390ad79c413ec779ff7a1ad2b86ff0fe389c085d)
- **ssl:** add function to free the verify callback function [62dc81a](https://github.com/fffonion/lua-resty-openssl/commit/62dc81a4c7be1c745e7e3ab728f3e060c981f446)
<a name="0.6.10"></a>
## [0.6.10] - 2021-01-12
### bug fixes
- **ecx:** return nil, err in set_parameters [98acaee](https://github.com/fffonion/lua-resty-openssl/commit/98acaeeeaa60dffd93a934f4fbf7ddfd8e9e9652)
- **pkey:** use named_curve encoding for EC group [1e65d9d](https://github.com/fffonion/lua-resty-openssl/commit/1e65d9d4b71c0e9c5f4d404e640a96e03902fd30)
### features
- **pkcs12:** allow to define algorithm to encrypt key and cert [b9678ce](https://github.com/fffonion/lua-resty-openssl/commit/b9678ce4ee4a233fb0bd8ed61d41c6d45a6fbb9d)
- **pkcs12:** check on cert and key mismatch [5953cc2](https://github.com/fffonion/lua-resty-openssl/commit/5953cc281cff06027f3b2bba23402e2915fd3ae1)
- **pkcs12:** encode and decode for pkcs12 [1467579](https://github.com/fffonion/lua-resty-openssl/commit/1467579fbe253996570dd188f580b98b8eb1db98)
- **pkey:** add is_private function to check if it's a private key [eb6cc1c](https://github.com/fffonion/lua-resty-openssl/commit/eb6cc1c2d5f7698c2641950d745a78da7baa6225)
- **ssl:** add the ssl and ssl_ctx module [40f3999](https://github.com/fffonion/lua-resty-openssl/commit/40f39994446a4cb954fc516f7047194cbf1141f8)
<a name="0.6.9"></a>
## [0.6.9] - 2020-11-09
### bug fixes
- **\*:** not mutating tables when doing pairs to avoid missing of iterration [836d5c9](https://github.com/fffonion/lua-resty-openssl/commit/836d5c915b27c0e63782c47effae16515ba71fed)
- **pkey:** fix typo in paramgen error message [d341246](https://github.com/fffonion/lua-resty-openssl/commit/d341246b5db5f912a3bcb06b7be1d08ffee093b3)
- **tests:** openssl3.0 alpha7 [5caa0e6](https://github.com/fffonion/lua-resty-openssl/commit/5caa0e60193ea535d0c0f1fe8491bc6779c9e720)
- **x509.altname:** organize GC handling better [f5a138c](https://github.com/fffonion/lua-resty-openssl/commit/f5a138c8b10dd285d9cacb6f2b3877b7831d0fba)
### features
- **provider:** add the provider module [dff92af](https://github.com/fffonion/lua-resty-openssl/commit/dff92af37102b094f1187914a0c76b6635130626)
- **x509.\*:** add get_signature_nid and get_signature_name [a35ae0a](https://github.com/fffonion/lua-resty-openssl/commit/a35ae0af6ad98251d4226e0daceab07c2832fc17)
<a name="0.6.8"></a>
## [0.6.8] - 2020-10-15
### bug fixes
- **pkey:** correctly free parameter after new parameters are set for RSA and DH keys on OpenSSL 1.0.2 [32d8c12](https://github.com/fffonion/lua-resty-openssl/commit/32d8c127f29e4ee0f13a8191f05f85ec74c2d8d4)
- **tests:** sort json in tests [aeeb7c3](https://github.com/fffonion/lua-resty-openssl/commit/aeeb7c3c2c7899b1b9c36b620476cca81b8eefdc)
### features
- **pkey:** allow to pass params for EC and DH keygen [e9aa7c7](https://github.com/fffonion/lua-resty-openssl/commit/e9aa7c751458134d03dfcda1318186cf3a691c1d)
- **pkey:** get and set DH parameters [ebaad8d](https://github.com/fffonion/lua-resty-openssl/commit/ebaad8d1e6533c9ad4980f557ead986104b947d0)
- **pkey:** support DH key and paramgen [f4661c6](https://github.com/fffonion/lua-resty-openssl/commit/f4661c6eb1d57d36daa93e8c86105b77ba8fe0cb)
- **pkey:** support one shot signing for all key types [79ca0d4](https://github.com/fffonion/lua-resty-openssl/commit/79ca0d43feda10894bfe5f0e72c4460dd4778c66)
<a name="0.6.7"></a>
## [0.6.7] - 2020-10-08
### features
- **pkey:** sign_raw and verify_recover [90ed1b6](https://github.com/fffonion/lua-resty-openssl/commit/90ed1b637729bfde33a94c6467327419186bdd38)
<a name="0.6.6"></a>
## [0.6.6] - 2020-09-29
### bug fixes
- **\*:** export tostring for x509.name and x509.altname [6143659](https://github.com/fffonion/lua-resty-openssl/commit/6143659706ea5b8c42a418b7fac1eae4179a6280)
- **kdf:** fix HKDF potential buffer overflow [da6f420](https://github.com/fffonion/lua-resty-openssl/commit/da6f42025c657f610f1ebee95f0489afd3628d9f)
- **x509.name:** potential memory leak in x509.name:find() [ac51fb1](https://github.com/fffonion/lua-resty-openssl/commit/ac51fb10581ec31e639c1298c080a899466fd57d)
- **x509.store:** return all error on load_file or add failure [a4ee237](https://github.com/fffonion/lua-resty-openssl/commit/a4ee2379802e41f5b5566ac11e59598d1f338ca5)
### features
- **x509.extension:** support create by ASN.1 octet string and nconf [7d8e81f](https://github.com/fffonion/lua-resty-openssl/commit/7d8e81f6789abd951f6e6b3aeb96607f8682c1d5)
<a name="0.6.5"></a>
## [0.6.5] - 2020-09-16
### bug fixes
- **\*:** x509.* set should return true on success [2a09575](https://github.com/fffonion/lua-resty-openssl/commit/2a09575425133e92c990513c7ea7445cf2ca47f4)
<a name="0.6.4"></a>
## [0.6.4] - 2020-08-27
### features
- **x509.csr:** finish {set,add}_extension functions [d34b702](https://github.com/fffonion/lua-resty-openssl/commit/d34b702a17b4f491e2a97e971da1d6125d482066)
- **x509.extension:** add ability to convert to other data type [15a5c7f](https://github.com/fffonion/lua-resty-openssl/commit/15a5c7ff38452a7bd04919b4a7e9c9dc1dfa931d)
<a name="0.6.3"></a>
## [0.6.3] - 2020-08-10
### bug fixes
- **\*:** cleanup and centralize ffi.typeof [5cbc247](https://github.com/fffonion/lua-resty-openssl/commit/5cbc2475bc5926fb0e4aa1b3e5b592144518d013)
- **\*:** remove hack for openssl 3.0 around broken EVP_PKEY_base_id [33181c3](https://github.com/fffonion/lua-resty-openssl/commit/33181c34210fb16c4190e88e1892fb19952420b2)
- **cipher:** use CipherFinal_ex and make test more robust [61fa022](https://github.com/fffonion/lua-resty-openssl/commit/61fa0224fc8dca8a13f9c3ae6904e6cb71c00c6b)
- **openssl:** correctly check error for getting version num ([#6](https://github.com/fffonion/lua-resty-openssl/issues/6)) [6a4b9e6](https://github.com/fffonion/lua-resty-openssl/commit/6a4b9e636714e81d405b934868ef347b3c803674)
- **tests:** pin lua-nginx-module and lua-resty-core [010b37e](https://github.com/fffonion/lua-resty-openssl/commit/010b37eb273da7b96ef39f95a6990357ecf49e49)
- **tests:** make pkey parameter test less flaky [d023edc](https://github.com/fffonion/lua-resty-openssl/commit/d023edcba56e5832b04e2ee0d84195c69a6258d4)
- **x509.\*:** pass correct digest parameter to sign [982ad48](https://github.com/fffonion/lua-resty-openssl/commit/982ad48594444994d5c5b98ba9ca3d139ce96f8c)
### features
- **\*:** support reset for hmac and digest [37ba4b0](https://github.com/fffonion/lua-resty-openssl/commit/37ba4b0f63c60898ee25cfeeeab8b5651c62296e)
- **\*:** initial support for OpenSSL 3.0 [be5dc10](https://github.com/fffonion/lua-resty-openssl/commit/be5dc10c24aabb6697ecb9fe2bd75c8a11e2b2d7)
- **x509.csr:** add get_extension and get_extensions function [638ca46](https://github.com/fffonion/lua-resty-openssl/commit/638ca46ecf1a4fdacac6e24abaea7d19db93c98b)
- **x509.extensions:** finish the stack implementation [f4cf725](https://github.com/fffonion/lua-resty-openssl/commit/f4cf7256e9cce0a280fab46d356ca5fcf3a48b4f)
- **x509.revoked:** add the x509.revoked module [58f0ce1](https://github.com/fffonion/lua-resty-openssl/commit/58f0ce11f2a39cdaabf1c9ba38ea7587adf8f25a)
<a name="0.6.2"></a>
## [0.6.2] - 2020-05-13
### bug fixes
- **\*:** add prefix to all error messages [8f52c25](https://github.com/fffonion/lua-resty-openssl/commit/8f52c2583b87ae0e66e9546f5db03d8fe667cbd4)
### features
- **cipher:** AEAD modes with authentication [fd7471e](https://github.com/fffonion/lua-resty-openssl/commit/fd7471e3a011519df0250681ee1bf82d61b1f154)
- **pkey:** support one shot sign/verify for Ed25519 and Ed448 keys [2565e85](https://github.com/fffonion/lua-resty-openssl/commit/2565e85337325f9cee7d601220120b185a22c430)
- **pkey:** support key derivation for EC, X25519 and X448 keys [0c0d941](https://github.com/fffonion/lua-resty-openssl/commit/0c0d9417711f4c9b513ae02382ea6f9f68f750fd)
- **pkey:** output pkey to DER and JWK format [8da24a5](https://github.com/fffonion/lua-resty-openssl/commit/8da24a5cd9241c09f51c610164dee5daffdd9129)
- **pkey:** load EC key from JWK format [df0c06f](https://github.com/fffonion/lua-resty-openssl/commit/df0c06f1e07be3c6e46d9d2a86005361ad386f83)
- **pkey:** set/get_parameters for EC key [67d54c8](https://github.com/fffonion/lua-resty-openssl/commit/67d54c8dc8555870bbf3fb216b3c636f3d9b220d)
- **pkey:** load RSA key from JWK format [dc118b3](https://github.com/fffonion/lua-resty-openssl/commit/dc118b3aec2a9ff26fc3f615a1569525cbc13dd4)
- **pkey:** add function to set rsa parameter [867fa10](https://github.com/fffonion/lua-resty-openssl/commit/867fa109863a2fd770f26a44b15cbea9d422b5cb)
<a name="0.6.1"></a>
## [0.6.1] - 2020-05-08
### bug fixes
- **x509:** fail soft when CRL is not set [2f2eb5e](https://github.com/fffonion/lua-resty-openssl/commit/2f2eb5edc78e3aa892eb36bd1b091c42ddc64480)
<a name="0.6.0"></a>
## [0.6.0] - 2020-03-11
### features
- **bn:** mathematics, bit shift and comparasion operations [87bf557](https://github.com/fffonion/lua-resty-openssl/commit/87bf5575a3643e11814b9c7be68ec78ce05011fe)
- **kdf:** use give id as type parameter [0e767d0](https://github.com/fffonion/lua-resty-openssl/commit/0e767d006f4561788d826eef82b753093f06ef9e)
- **kdf:** kdf.derive in luaossl compat mode [45788b6](https://github.com/fffonion/lua-resty-openssl/commit/45788b6ea742755b31d6b361950f3ea5d5d24bdf)
<a name="0.6.0-rc.0"></a>
## [0.6.0-rc.0] - 2020-03-02
### features
- **altname:** RFC822 alias to email [37467fc](https://github.com/fffonion/lua-resty-openssl/commit/37467fcf83093d0c99251f43a4cc916d5c934eda)
- **kdf:** add key derivation functions support [d78835e](https://github.com/fffonion/lua-resty-openssl/commit/d78835e861df4b7f79bb0fe5e17a2f19be1e0d3f)
<a name="0.5.4"></a>
## [0.5.4] - 2020-02-27
### bug fixes
- **store:** set X509_V_FLAG_CRL_CHECK flag if a crl is added [88574d5](https://github.com/fffonion/lua-resty-openssl/commit/88574d5ecef0f75a293cd7d23b764d629905e3df)
- **x509.\*:** returns soft error if extension is not found [a0a75aa](https://github.com/fffonion/lua-resty-openssl/commit/a0a75aa2644203e22461aa1dd09ef8672e2ba576)
<a name="0.5.3"></a>
## [0.5.3] - 2020-02-22
### features
- **openssl:** lua-resty-hmac compat [fad844f](https://github.com/fffonion/lua-resty-openssl/commit/fad844f804abe8d73b7d4b7655d562fdb3d84ebf)
<a name="0.5.2"></a>
## [0.5.2] - 2020-02-09
### bug fixes
- **pkey:** decrease copy by 1 when generating key [bcc38e9](https://github.com/fffonion/lua-resty-openssl/commit/bcc38e9fc5e733a8f3f9d09e5eef1e2eb3c15d4d)
### features
- **x509.extension:** allow to create an extension by NID [6d66a2d](https://github.com/fffonion/lua-resty-openssl/commit/6d66a2d9fa7cc36cc2e6c85a78ad2236e525f3b0)
<a name="0.5.1"></a>
## [0.5.1] - 2020-02-04
### bug fixes
- **x509.crl:** fix creating empty crl instance [046ca36](https://github.com/fffonion/lua-resty-openssl/commit/046ca36228f639c191c81a7b84dfedfc523d0340)
### features
- **pkey:** load encrypted PEM key [7fa7a29](https://github.com/fffonion/lua-resty-openssl/commit/7fa7a29882bbcef294f83cd1f66b9960344a0e07)
- **x509.extension:** add tostring() as synonym to text() [87c162d](https://github.com/fffonion/lua-resty-openssl/commit/87c162de9fa7bb3e3930bd760ff7dfece30f1b49)
<a name="0.5.0"></a>
## [0.5.0] - 2020-02-03
### bug fixes
- **\*:** add missing crl.dup function, organize store:add gc handler [6815e5d](https://github.com/fffonion/lua-resty-openssl/commit/6815e5df04fdb77c83b0345f166664759a573962)
- **asn1:** support GENERALIZEDTIME string format [8c7e2d6](https://github.com/fffonion/lua-resty-openssl/commit/8c7e2d67857cb6875cf52fadf43cadf05d8c5c40)
- **error:** return latest error string not earliest in some cases [0b5955d](https://github.com/fffonion/lua-resty-openssl/commit/0b5955d4cb73f3c7d3321ed7384ae862640a6a7f)
- **stack:** protective over first argument [bf455ff](https://github.com/fffonion/lua-resty-openssl/commit/bf455ff310b94b26a3bed513ffc9f308f65691ed)
- **x509:** guard around oscp stack index [1b59b85](https://github.com/fffonion/lua-resty-openssl/commit/1b59b8565b5dee4cb1dd14d22bc24ec04dfbf3d6)
- **x509.store:** correctly save x509 instance references [d8d755f](https://github.com/fffonion/lua-resty-openssl/commit/d8d755f7a281ad09d896a1d78ad9e53f6c028bdc)
### features
- **\*:** add iterater and helpers for stack-like objects [46bb723](https://github.com/fffonion/lua-resty-openssl/commit/46bb7237028a16e67878d8310c25e908ceece009)
- **autogen:** generate tests for x509, csr and crl [1392428](https://github.com/fffonion/lua-resty-openssl/commit/1392428352164d2a1a6e0c03075ff65b55aecdee)
- **objects:** add helper function for ASN1_OBJECT [d037706](https://github.com/fffonion/lua-resty-openssl/commit/d037706c11d716afe3616bdaf4658afc1763081d)
- **pkey:** asymmetric encryption and decryption [6d60451](https://github.com/fffonion/lua-resty-openssl/commit/6d60451157edbf9cefb634f888dfa3e6d9be302f)
- **x509:** getter/setters for extensions [243f40d](https://github.com/fffonion/lua-resty-openssl/commit/243f40d35562a516f404188a5c7eb8f5134d9b30)
- **x509:** add get_ocsp_url and get_crl_url [6141b6f](https://github.com/fffonion/lua-resty-openssl/commit/6141b6f5aed38706b477a71d8c4383bf55da7eee)
- **x509.altname:** support iterate and decode over the stack [083a201](https://github.com/fffonion/lua-resty-openssl/commit/083a201746e02d51f6c5c640ad9bf8c6730ebe0b)
- **x509.crl:** add crl module [242f8cb](https://github.com/fffonion/lua-resty-openssl/commit/242f8cb45d6c2df5918f26540c92a430d42feb5d)
- **x509.csr:** autogen some csr functions as well [9800e36](https://github.com/fffonion/lua-resty-openssl/commit/9800e36c2ff8a299b88f24091cc722940a8652bb)
- **x509.extension:** decode object, set/get critical flag and get text representation [8cb585f](https://github.com/fffonion/lua-resty-openssl/commit/8cb585fc51de04065cd7eeeea06e6240e7251614)
- **x509.extension:** add x509.extension.dist_points and x509.extension.info_access [63d3992](https://github.com/fffonion/lua-resty-openssl/commit/63d3992163144ed75474a8046398d605570c30b7)
<a name="0.4.4"></a>
## [0.4.4] - 2020-02-27
### bug fixes
- **pkey:** clean up errors when trying loading key types [7b3d351](https://github.com/fffonion/lua-resty-openssl/commit/7b3d3513cfb7a8800f49dbdd3ca521b4dadefbad)
<a name="0.4.3"></a>
## [0.4.3] - 2020-01-15
### bug fixes
- **asn1:** support GENERALIZEDTIME string format [cc6326f](https://github.com/fffonion/lua-resty-openssl/commit/cc6326fed1bc53e64042d4742208ed68d7bb42ac)
<a name="0.4.2"></a>
## [0.4.2] - 2020-01-06
### bug fixes
- **bn:** memory leak in bn:to_hex [6718e9e](https://github.com/fffonion/lua-resty-openssl/commit/6718e9e76a8410c78b32e5abf6d06a628fe8dc8b)
- **compat:** refine luaossl compat mode [0d86eb5](https://github.com/fffonion/lua-resty-openssl/commit/0d86eb58848e970408bec7ee9d77102c241c3a5c)
- **openssl:** typo in luaossl_compat [#1](https://github.com/fffonion/lua-resty-openssl/issues/1) [1c3ea60](https://github.com/fffonion/lua-resty-openssl/commit/1c3ea60877d1532eaaddc13ab3be1550c4c5a7f1)
- **x509:** memory leak in x509:set_not_(before|after) [b4a32f8](https://github.com/fffonion/lua-resty-openssl/commit/b4a32f82c33107d2db729caa06aee141b7f9a016)
- **x509:** and missing x509.get_serial_number code [e7d0fb6](https://github.com/fffonion/lua-resty-openssl/commit/e7d0fb6eace77d357d19043b88bb765ec29a5193)
- **x509.csr:** correctly gc extension [ece5be3](https://github.com/fffonion/lua-resty-openssl/commit/ece5be3f517b69563150973e7da6063d5826a9ad)
- **x509.store:** memory leak in store:add [57815dd](https://github.com/fffonion/lua-resty-openssl/commit/57815dd38bbb2e260e8cdf3e8ddac48d6254b8fc)
<a name="0.4.1"></a>
## [0.4.1] - 2019-12-24
### bug fixes
- **x509:** correct X509_add1_ext_i2d include path [b08b312](https://github.com/fffonion/lua-resty-openssl/commit/b08b3123a0fb2770296f04c830414bd38588e8eb)
### features
- **x509:** getters for basic constraints and basic constraints critical [82f5725](https://github.com/fffonion/lua-resty-openssl/commit/82f5725d4738b3bf83fcbf3154fe5979fe8d1af4)
<a name="0.4.0"></a>
## [0.4.0] - 2019-12-20
### bug fixes
- **\*:** always return ok, err if there's no explict return value [3e68167](https://github.com/fffonion/lua-resty-openssl/commit/3e681676f85e26c8c7af6f72a2c4afcb98952cd6)
- **evp:** correct ptr naming [72f8765](https://github.com/fffonion/lua-resty-openssl/commit/72f8765250861d6504a767da81afe19c2d2896a4)
### features
- **\*:** add x509.digest and bn.to_hex [11ea9ae](https://github.com/fffonion/lua-resty-openssl/commit/11ea9aebca6bb5c354ad94525bd2e264debfebbd)
- **version:** add function to print human readable version [7687573](https://github.com/fffonion/lua-resty-openssl/commit/76875731011e5641eef9881ace2becf1bf057cfd)
- **x509:** add x509 stack (chain) support [72154fc](https://github.com/fffonion/lua-resty-openssl/commit/72154fcb7686ce5a754d4fe4f121f07507a1513e)
- **x509.chain:** allow to duplicate a stack [3fa19b7](https://github.com/fffonion/lua-resty-openssl/commit/3fa19b79509c73cf5dce6e3445cbf90a9466d656)
- **x509.name:** allow to iterate over objects and find objects [714a1e5](https://github.com/fffonion/lua-resty-openssl/commit/714a1e541e0ce3ffb33d257d9af50ae628094fb2)
- **x509.store:** support certificate verification [c9dd4bf](https://github.com/fffonion/lua-resty-openssl/commit/c9dd4bf8065a51a97fcf940c33ba046b73ac2049)
<a name="0.3.0"></a>
## [0.3.0] - 2019-12-12
### bug fixes
- **\*:** move cdef and macros to seperate file [28c3390](https://github.com/fffonion/lua-resty-openssl/commit/28c339085383bfbcb72e192701b96e08fb4344f0)
- **\*:** normalize error handling [ff18d54](https://github.com/fffonion/lua-resty-openssl/commit/ff18d54d2b4402de3bc02731f99c32a9953f8784)
### features
- **cipher:** add symmetric cryptography support [9b89e8d](https://github.com/fffonion/lua-resty-openssl/commit/9b89e8dcc1489832c893373150bfeef6a838da34)
- **hmac:** add hmac support [5cc2a15](https://github.com/fffonion/lua-resty-openssl/commit/5cc2a15ce43a9c1c73dccf1a15232ee2a9108460)
<a name="0.2.1"></a>
## [0.2.1] - 2019-10-22
### bug fixes
- **x509:** decrease by set_version by 1 per standard [b6ea5b9](https://github.com/fffonion/lua-resty-openssl/commit/b6ea5b933aadfb8284f7486eb33d8a4c21b7a6de)
<a name="0.2.0"></a>
## 0.2.0 - 2019-10-18
### bug fixes
- **\*:** fix working and name test [f6db7ef](https://github.com/fffonion/lua-resty-openssl/commit/f6db7ef3c1ce5f9a75f01dc24f904fd8942c7897)
- **\*:** normalize naming, explictly control cdef for different openssl versions [c626b53](https://github.com/fffonion/lua-resty-openssl/commit/c626b538c2dc33272b130503ddd21f21bd9d995f)
- **\*:** cleanup cdef [3c02d02](https://github.com/fffonion/lua-resty-openssl/commit/3c02d020822a30fdf7dae0aa7c6aa47c4660aea8)
- **\*:** test cdata type before passing in ffi [de99069](https://github.com/fffonion/lua-resty-openssl/commit/de99069e40c075844a15b91720e2d5c9c9a68dd7)
### features
- **\*:** add more x509 API, and rand bytes generator [6630fde](https://github.com/fffonion/lua-resty-openssl/commit/6630fde2e5e9f367e4652dc390678d4eeb57ad5d)
- **error:** add ability to pull error description [d19ece9](https://github.com/fffonion/lua-resty-openssl/commit/d19ece993ac797fdf6708400cf83e3f4ed0bb9f4)
- **x509:** generate certificate [9b4f59b](https://github.com/fffonion/lua-resty-openssl/commit/9b4f59bf94647aab37da6b8076ee99e155ba8023)
- **x509:** export pubkey [ede4f81](https://github.com/fffonion/lua-resty-openssl/commit/ede4f817cb0fe092ad6f9ab5d6ecdcde864a9fd8)
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.21...HEAD
[0.8.21]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.20...0.8.21
[0.8.20]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.19...0.8.20
[0.8.19]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.18...0.8.19
[0.8.18]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.17...0.8.18
[0.8.17]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.16...0.8.17
[0.8.16]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.15...0.8.16
[0.8.15]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.14...0.8.15
[0.8.14]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.13...0.8.14
[0.8.13]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.11...0.8.13
[0.8.11]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.10...0.8.11
[0.8.10]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.9...0.8.10
[0.8.9]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.8...0.8.9
[0.8.8]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.7...0.8.8
[0.8.7]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.6...0.8.7
[0.8.6]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.5...0.8.6
[0.8.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.4...0.8.5
[0.8.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.3...0.8.4
[0.8.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.2...0.8.3
[0.8.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.1...0.8.2
[0.8.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.0...0.8.1
[0.8.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.5...0.8.0
[0.7.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.4...0.7.5
[0.7.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.3...0.7.4
[0.7.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.2...0.7.3
[0.7.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.1...0.7.2
[0.7.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.11...0.7.0
[0.6.11]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.10...0.6.11
[0.6.10]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.9...0.6.10
[0.6.9]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.8...0.6.9
[0.6.8]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.7...0.6.8
[0.6.7]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.6...0.6.7
[0.6.6]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.5...0.6.6
[0.6.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.4...0.6.5
[0.6.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.3...0.6.4
[0.6.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.2...0.6.3
[0.6.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.1...0.6.2
[0.6.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.0...0.6.1
[0.6.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.0-rc.0...0.6.0
[0.6.0-rc.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.4...0.6.0-rc.0
[0.5.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.3...0.5.4
[0.5.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.2...0.5.3
[0.5.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.1...0.5.2
[0.5.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.0...0.5.1
[0.5.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.4...0.5.0
[0.4.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.3...0.4.4
[0.4.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.2...0.4.3
[0.4.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.1...0.4.2
[0.4.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.0...0.4.1
[0.4.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.3.0...0.4.0
[0.3.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.2.1...0.3.0
[0.2.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.2.0...0.2.1

View File

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2020, Wangchong Zhou
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,18 @@
OPENRESTY_PREFIX=/usr/local/openresty
#LUA_VERSION := 5.1
PREFIX ?= /usr/local
LUA_INCLUDE_DIR ?= $(PREFIX)/include
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
.PHONY: all test install
all: ;
install: all
cp -rpv lib/resty/openssl/. $(DESTDIR)$(LUA_LIB_DIR)/resty/openssl
test: all
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
name = lua-resty-openssl
abstract = FFI-based OpenSSL binding for LuaJIT
author = fffonion
is_original = yes
license = 3bsd
lib_dir = lib
doc_dir = lib
repo_link = https://github.com/fffonion/lua-resty-openssl
main_module = lib/resty/openssl.lua
requires = luajit
exclude_files=*.rock, *.rockspec

View File

@ -0,0 +1,110 @@
local key = string.rep("0", 32)
local iv = string.rep("0", 12)
local to_be_encrypted = "secret"
local aad = "aead aad"
local mode = "aes-256-gcm"
-- local mode = "chacha20-poly1305"
ngx.say("use cipher ", mode)
-- using one shot interface
local cipher = assert(require("resty.openssl.cipher").new(mode))
local encrypted = assert(cipher:encrypt(key, iv, to_be_encrypted, false, aad))
-- OR using streaming interface
assert(cipher:init(key, iv, {
is_encrypt = true,
}))
assert(cipher:update_aead_aad(aad))
encrypted = assert(cipher:final(to_be_encrypted))
ngx.say("encryption result: ", ngx.encode_base64(encrypted))
local tag = assert(cipher:get_aead_tag())
ngx.say("tag is: ", ngx.encode_base64(tag), " ", #tag)
local _, err = cipher:decrypt(key, iv, encrypted, false, nil, tag)
if err then
ngx.say("no AAD, decryption failed")
end
local _, err = cipher:decrypt(key, iv, encrypted, false, "wrong", tag)
if err then
ngx.say("wrong AAD, decryption failed")
end
-- this seems not working for chacha20-poly1305
local _, err = cipher:decrypt(key, iv, encrypted, false, aad, nil)
if err then
ngx.say("no tag, decryption failed")
end
local _, err = cipher:decrypt(key, iv, encrypted, false, aad, "wrong")
if err then
ngx.say("wrong tag, decryption failed")
end
-- using one shot interface
local decrypted = assert(cipher:decrypt(key, iv, encrypted, false, aad, tag))
-- OR using streaming interface
assert(cipher:init(key, iv, {
is_encrypt = false,
}))
assert(cipher:update_aead_aad(aad))
assert(cipher:set_aead_tag(tag))
decrypted = assert(cipher:final(encrypted))
ngx.say("decryption result: ", decrypted)
--[[
Note in some implementations like `libsodium` or Java, AEAD ciphers append the `tag` (or `MAC`)
at the end of encrypted ciphertext. In such case, user will need to manually cut off the `tag`
with correct size(usually 16 bytes) and pass in the ciphertext and `tag` seperately.
-- encrypt with libsodium and decrypt in lua-resty-openssl
<? php
$encrypted_with_tag = sodium_crypto_aead_aes256gcm_encrypt(
$to_be_encrypted,
$aad,
$iv,
$key
);
?>
local tag = string.sub(encrypted_with_tag, #encrypted_with_tag-16, #encrypted_with_tag)
local encrypted = string.sub(encrypted_with_tag, 1, #encrypted_with_tag-16)
local decrypted = assert(cipher:decrypt(key, iv, encrypted, false, aad, tag))
-- encrypt with lua-resty-openssl and decrypt in libsodium
local encrypted = assert(cipher:encrypt(key, iv, to_be_encrypted, false, aad))
local tag = assert(cipher:get_aead_tag())
<? php
$decrypted = sodium_crypto_aead_aes256gcm_decrypt(
$encrypted . $tag,
$aad,
$iv,
$key
);
?>
]]--
--[[
If the encryption is not done properly, it's possible that no tag is provided after all.
In such case, use the streaming interface and call update() instead of final()
]]
assert(cipher:init(key, iv, {
is_encrypt = false,
}))
assert(cipher:update_aead_aad(aad))
decrypted = assert(cipher:update(encrypted))
ngx.say("decryption result (without checking MAC): ", decrypted)

View File

@ -0,0 +1,74 @@
-- sample function to generate a DER CSR from given pkey and domains
local function create_csr(domain_pkey, ...)
local domains = {...}
local subject = require("resty.openssl.x509.name").new()
local _, err = subject:add("CN", domains[1])
if err then
return nil, err
end
local alt, err
if #{...} > 1 then
alt, err = require("resty.openssl.x509.altname").new()
if err then
return nil, err
end
for _, domain in pairs(domains) do
_, err = alt:add("DNS", domain)
if err then
return nil, err
end
end
end
local csr = require("resty.openssl.x509.csr").new()
local _
_, err = csr:set_subject_name(subject)
if err then
return nil, err
end
if alt then
_, err = csr:set_subject_alt_name(alt)
if err then
return nil, err
end
end
_, err = csr:set_pubkey(domain_pkey)
if err then
return nil, err
end
_, err = csr:sign(domain_pkey)
if err then
return nil, err
end
return csr:tostring("DER"), nil
end
-- create a EC key
local pkey, err = require("resty.openssl.pkey").new({
type = 'EC',
curve = 'prime256v1',
})
if err then
error(err)
end
-- create a CSR using the key
local der, err = create_csr(pkey, "example.com", "*.example.com")
if err then
error(err)
end
-- use openssl cli to see csr we just generated
local f = io.open("example.csr", "w")
f:write(der)
f:close()
os.execute("openssl req -in example.csr -inform der -noout -text")
os.remove("example.csr")

View File

@ -0,0 +1,52 @@
local version=require "resty.openssl.version"
print("VERSION:")
local version_table = {
"VERSION",
"CFLAGS",
"BUILT_ON",
"PLATFORM",
"DIR",
"ENGINES_DIR",
"VERSION_STRING",
"FULL_VERSION_STRING",
"MODULES_DIR",
"CPU_INFO",
}
for _, k in ipairs(version_table) do
print(string.format("%20s: %s", k, version.version(version[k])))
end
print(string.rep("-", 64))
if version.OPENSSL_3X then
print("INFO:")
local info_table = {
"INFO_CONFIG_DIR",
"INFO_ENGINES_DIR",
"INFO_MODULES_DIR",
"INFO_DSO_EXTENSION",
"INFO_DIR_FILENAME_SEPARATOR",
"INFO_LIST_SEPARATOR",
"INFO_SEED_SOURCE",
"INFO_CPU_SETTINGS",
}
for _, k in ipairs(info_table) do
print(string.format("%20s: %s", k, version.info(version[k])))
end
print(string.rep("-", 64))
print("PROVIDER:")
local pro = require "resty.openssl.provider"
for _, n in ipairs({"default", "legacy", "fips", "null"}) do
local ok, err = pro.load(n)
print(string.format("%10s load: %s", n, ok or err))
end
end

View File

@ -0,0 +1,17 @@
local pkey = require("resty.openssl.pkey")
local priv = assert(pkey.new())
local pub = assert(pkey.new(priv:to_PEM("public")))
local original = "original text"
-- same as nodejs: crypto.privateEncrypt
-- php: openssl_private_encrypt
local digested = assert(priv:sign_raw(original))
print("Digested message: " .. ngx.encode_base64(digested))
-- same as nodejs: crypto.publicDecrypt
-- php: openssl_public_decrypt
local recovered = assert(pub:verify_recover(digested))
print("Recovered message: " .. recovered)

View File

@ -0,0 +1,76 @@
local openssl_bignum = require "resty.openssl.bn"
local openssl_rand = require "resty.openssl.rand"
local openssl_pkey = require "resty.openssl.pkey"
local x509 = require "resty.openssl.x509"
local x509_extension = require "resty.openssl.x509.extension"
local x509_name = require "resty.openssl.x509.name"
-- taken from https://github.com/Kong/kong/blob/master/kong/cmd/utils/prefix_handler.lua
local function generate_self_signed()
local key = openssl_pkey.new { bits = 2048 }
local crt = x509.new()
assert(crt:set_pubkey(key))
assert(crt:set_version(3))
assert(crt:set_serial_number(openssl_bignum.from_binary(openssl_rand.bytes(16))))
-- last for 20 years
local now = os.time()
assert(crt:set_not_before(now))
assert(crt:set_not_after(now + 86400 * 20 * 365))
local name = assert(x509_name.new()
:add("C", "US")
:add("ST", "California")
:add("L", "San Francisco")
:add("O", "Kong")
:add("OU", "IT Department")
:add("CN", "localhost"))
assert(crt:set_subject_name(name))
assert(crt:set_issuer_name(name))
-- Not a CA
assert(crt:set_basic_constraints { CA = false })
assert(crt:set_basic_constraints_critical(true))
-- Only allowed to be used for TLS connections (client or server)
assert(crt:add_extension(x509_extension.new("extendedKeyUsage",
"serverAuth,clientAuth")))
-- RFC-3280 4.2.1.2
assert(crt:add_extension(x509_extension.new("subjectKeyIdentifier", "hash", {
subject = crt
})))
-- All done; sign
assert(crt:sign(key))
return crt, key
end
local crt, key = generate_self_signed()
do -- write key out
local fd = assert(io.open("key.pem", "w+b"))
local pem = assert(key:to_PEM("private"))
assert(fd:write(pem))
fd:close()
end
print("================== private key =================")
os.execute("openssl pkey -in key.pem -noout -text")
os.remove("key.pem")
do -- write cert out
local fd = assert(io.open("cert.pem", "w+b"))
local pem = assert(crt:to_PEM("private"))
assert(fd:write(pem))
fd:close()
end
print("\n\n")
print("================== certificate =================")
os.execute("openssl x509 -in cert.pem -noout -text")
os.remove("cert.pem")

View File

@ -0,0 +1,58 @@
local pkey = require("resty.openssl.pkey")
local digest = require("resty.openssl.digest")
local x509 = require("resty.openssl.x509")
local altname = require("resty.openssl.x509.altname")
local extension = require("resty.openssl.x509.extension")
local objects = require("resty.openssl.objects")
-- creates the ACME Identifier NID into openssl's internal lookup table
-- if it doesn't exist
local id_pe_acmeIdentifier = "1.3.6.1.5.5.7.1.31"
local nid = objects.txt2nid(id_pe_acmeIdentifier)
if not nid or nid == 0 then
nid = objects.create(
id_pe_acmeIdentifier, -- nid
"pe-acmeIdentifier", -- sn
"ACME Identifier" -- ln
)
end
-- generate the tls-alpn-01 challenge certificate/key per
-- https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07
-- with given domain name and challenge token
local function serve_challenge_cert(domain, challenge)
local dgst = assert(digest.new("sha256"):final(challenge))
-- There're two ways to set ASN.1 octect string to the extension
-- The recommanded way is to pass the string directly to extension.from_der()
local _, err = extension.from_der(dgst, nid, true)
if err then
return nil, nil, err
end
-- OR we put the ASN.1 signature for this string by ourselves
-- 0x04: OCTET STRING
-- 0x20: length
local dgst_hex = "DER:0420" .. dgst:gsub("(.)", function(s) return string.format("%02x", string.byte(s)) end)
local ext, err = extension.new(nid, dgst_hex)
if err then
return nil, nil, err
end
ext:set_critical(true)
local key = pkey.new()
local cert = x509.new()
cert:set_pubkey(key)
cert:add_extension(ext)
local alt = assert(altname.new():add(
"DNS", domain
))
local _, err = cert:set_subject_alt_name(alt)
if err then
return nil, nil, err
end
cert:sign(key)
return key, cert
end

View File

@ -0,0 +1,31 @@
local pkey = require("resty.openssl.pkey")
-- alice's private key
local alice_priv = assert(pkey.new({
type = "X25519"
}))
-- alice's public key, shared with bob
local alice_pub = assert(pkey.new(alice_priv:to_PEM("public")))
-- bob's private key
local bob_priv = assert(pkey.new({
type = "X25519"
}))
-- bobs' public key, shared with alice
local bob_pub = assert(pkey.new(bob_priv:to_PEM("public")))
ngx.say("alice and bob hold ",
alice_priv:to_PEM() == bob_priv:to_PEM() and "same keys" or "different keys")
ngx.say("")
local k1 = assert(alice_priv:derive(bob_pub))
ngx.say("alice use this key to talk with bob: ", ngx.encode_base64(k1))
local k2 = assert(bob_priv:derive(alice_pub))
ngx.say("bob use this key to talk with alice: ", ngx.encode_base64(k2))
ngx.say("")
ngx.say(k1 == k2 and "key exchange is correct" or "key exchange is not correct")

View File

@ -0,0 +1,476 @@
local ffi = require("ffi")
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X, BORINGSSL
local function try_require_modules()
package.loaded["resty.openssl.version"] = nil
local pok, lib = pcall(require, "resty.openssl.version")
if pok then
OPENSSL_3X = lib.OPENSSL_3X
BORINGSSL = lib.BORINGSSL
require "resty.openssl.include.crypto"
require "resty.openssl.include.objects"
else
package.loaded["resty.openssl.version"] = nil
end
end
try_require_modules()
local _M = {
_VERSION = '0.8.21',
}
local libcrypto_name
local lib_patterns = {
"%s", "%s.so.3", "%s.so.1.1", "%s.so.1.0"
}
function _M.load_library()
for _, pattern in ipairs(lib_patterns) do
-- true: load to global namespae
local pok, _ = pcall(ffi.load, string.format(pattern, "crypto"), true)
if pok then
libcrypto_name = string.format(pattern, "crypto")
ffi.load(string.format(pattern, "ssl"), true)
try_require_modules()
return libcrypto_name
end
end
return false, "unable to load crypto library"
end
function _M.load_modules()
_M.bn = require("resty.openssl.bn")
_M.cipher = require("resty.openssl.cipher")
_M.digest = require("resty.openssl.digest")
_M.hmac = require("resty.openssl.hmac")
_M.kdf = require("resty.openssl.kdf")
_M.pkey = require("resty.openssl.pkey")
_M.objects = require("resty.openssl.objects")
_M.rand = require("resty.openssl.rand")
_M.version = require("resty.openssl.version")
_M.x509 = require("resty.openssl.x509")
_M.altname = require("resty.openssl.x509.altname")
_M.chain = require("resty.openssl.x509.chain")
_M.csr = require("resty.openssl.x509.csr")
_M.crl = require("resty.openssl.x509.crl")
_M.extension = require("resty.openssl.x509.extension")
_M.extensions = require("resty.openssl.x509.extensions")
_M.name = require("resty.openssl.x509.name")
_M.revoked = require("resty.openssl.x509.revoked")
_M.store = require("resty.openssl.x509.store")
_M.pkcs12 = require("resty.openssl.pkcs12")
_M.ssl = require("resty.openssl.ssl")
_M.ssl_ctx = require("resty.openssl.ssl_ctx")
if OPENSSL_3X then
_M.provider = require("resty.openssl.provider")
_M.mac = require("resty.openssl.mac")
_M.ctx = require("resty.openssl.ctx")
end
_M.bignum = _M.bn
end
function _M.luaossl_compat()
_M.load_modules()
_M.csr.setSubject = _M.csr.set_subject_name
_M.csr.setPublicKey = _M.csr.set_pubkey
_M.x509.setPublicKey = _M.x509.set_pubkey
_M.x509.getPublicKey = _M.x509.get_pubkey
_M.x509.setSerial = _M.x509.set_serial_number
_M.x509.getSerial = _M.x509.get_serial_number
_M.x509.setSubject = _M.x509.set_subject_name
_M.x509.getSubject = _M.x509.get_subject_name
_M.x509.setIssuer = _M.x509.set_issuer_name
_M.x509.getIssuer = _M.x509.get_issuer_name
_M.x509.getOCSP = _M.x509.get_ocsp_url
local pkey_new = _M.pkey.new
_M.pkey.new = function(a, b)
if type(a) == "string" then
return pkey_new(a, b and unpack(b))
else
return pkey_new(a, b)
end
end
_M.cipher.encrypt = function(self, key, iv, padding)
return self, _M.cipher.init(self, key, iv, true, not padding)
end
_M.cipher.decrypt = function(self, key, iv, padding)
return self, _M.cipher.init(self, key, iv, false, not padding)
end
local digest_update = _M.digest.update
_M.digest.update = function(self, ...)
local ok, err = digest_update(self, ...)
if ok then
return self
else
return nil, err
end
end
local store_verify = _M.store.verify
_M.store.verify = function(...)
local ok, err = store_verify(...)
if err then
return false, err
else
return true, ok
end
end
local kdf_derive = _M.kdf.derive
local kdf_keys_mappings = {
iter = "pbkdf2_iter",
key = "hkdf_key",
info = "hkdf_info",
secret = "tls1_prf_secret",
seed = "tls1_prf_seed",
maxmem_bytes = "scrypt_maxmem",
N = "scrypt_N",
r = "scrypt_r",
p = "scrypt_p",
}
_M.kdf.derive = function(o)
for k1, k2 in pairs(kdf_keys_mappings) do
o[k1] = o[k2]
o[k2] = nil
end
local hkdf_mode = o.hkdf_mode
if hkdf_mode == "extract_and_expand" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_AND_EXPAND
elseif hkdf_mode == "extract_only" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_ONLY
elseif hkdf_mode == "expand_only" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXPAND_ONLY
end
return kdf_derive(o)
end
_M.pkcs12.new = function(tbl)
local certs = {}
local passphrase = tbl.passphrase
if not tbl.key then
return nil, "key must be set"
end
for _, cert in ipairs(tbl.certs) do
if not _M.x509.istype(cert) then
return nil, "certs must contains only x509 instance"
end
if cert:check_private_key(tbl.key) then
tbl.cert = cert
else
certs[#certs+1] = cert
end
end
tbl.cacerts = certs
return _M.pkcs12.encode(tbl, passphrase)
end
_M.crl.add = _M.crl.add_revoked
_M.crl.lookupSerial = _M.crl.get_by_serial
for mod, tbl in pairs(_M) do
if type(tbl) == 'table' then
-- avoid using a same table as the iterrator will change
local new_tbl = {}
-- luaossl always error() out
for k, f in pairs(tbl) do
if type(f) == 'function' then
local of = f
new_tbl[k] = function(...)
local ret = { of(...) }
if ret and #ret > 1 and ret[#ret] then
error(mod .. "." .. k .. "(): " .. ret[#ret])
end
return unpack(ret)
end
end
end
for k, f in pairs(new_tbl) do
tbl[k] = f
end
setmetatable(tbl, {
__index = function(t, k)
local tok
-- handle special case
if k == 'toPEM' then
tok = 'to_PEM'
else
tok = k:gsub("(%l)(%u)", function(a, b) return a .. "_" .. b:lower() end)
if tok == k then
return
end
end
if type(tbl[tok]) == 'function' then
return tbl[tok]
end
end
})
end
end
-- skip error() conversion
_M.pkcs12.parse = function(p12, passphrase)
local r, err = _M.pkcs12.decode(p12, passphrase)
if err then error(err) end
return r.key, r.cert, r.cacerts
end
end
if OPENSSL_3X then
require "resty.openssl.include.evp"
local provider = require "resty.openssl.provider"
local ctx_lib = require "resty.openssl.ctx"
local fips_provider_ctx
function _M.set_fips_mode(enable, self_test)
if (not not enable) == _M.get_fips_mode() then
return true
end
if enable then
local p, err = provider.load("fips")
if not p then
return false, err
end
fips_provider_ctx = p
if self_test then
local ok, err = p:self_test()
if not ok then
return false, err
end
end
elseif fips_provider_ctx then -- disable
local p = fips_provider_ctx
fips_provider_ctx = nil
return p:unload()
end
-- set algorithm in fips mode in default ctx
-- this deny/allow non-FIPS compliant algorithms to be used from EVP interface
-- and redirect/remove redirect implementation to fips provider
if C.EVP_default_properties_enable_fips(ctx_lib.get_libctx(), enable and 1 or 0) == 0 then
return false, format_error("openssl.set_fips_mode: EVP_default_properties_enable_fips")
end
return true
end
function _M.get_fips_mode()
local pok = provider.is_available("fips")
if not pok then
return false
end
return C.EVP_default_properties_is_fips_enabled(ctx_lib.get_libctx()) == 1
end
else
function _M.set_fips_mode(enable)
if (not not enable) == _M.get_fips_mode() then
return true
end
if C.FIPS_mode_set(enable and 1 or 0) == 0 then
return false, format_error("openssl.set_fips_mode")
end
return true
end
function _M.get_fips_mode()
return C.FIPS_mode() == 1
end
end
function _M.set_default_properties(props)
if not OPENSSL_3X then
return nil, "openssl.set_default_properties is only not supported from OpenSSL 3.0"
end
local ctx_lib = require "resty.openssl.ctx"
if C.EVP_set_default_properties(ctx_lib.get_libctx(), props) == 0 then
return false, format_error("openssl.EVP_set_default_properties")
end
return true
end
local function list_legacy(typ, get_nid_cf)
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
require ("resty.openssl.include.evp." .. typ_lower)
local ret = {}
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_list_fn*",
function(elem, from, to, arg)
if elem ~= nil then
local nid = get_nid_cf(elem)
table.insert(ret, ffi_str(C.OBJ_nid2sn(nid)))
end
-- from/to (renamings) are ignored
end)
C[typ .. "_do_all_sorted"](fn, nil)
fn:free()
return ret
end
local function list_provided(typ)
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
local typ_ptr = typ .. "*"
require ("resty.openssl.include.evp." .. typ_lower)
local ctx_lib = require "resty.openssl.ctx"
local ret = {}
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_provided_list_fn*",
function(elem, _)
elem = ffi_cast(typ_ptr, elem)
local name = ffi_str(C[typ .. "_get0_name"](elem))
-- alternate names are ignored, retrieve use TYPE_names_do_all
local prov = ffi_str(C.OSSL_PROVIDER_get0_name(C[typ .. "_get0_provider"](elem)))
table.insert(ret, name .. " @ " .. prov)
end)
C[typ .. "_do_all_provided"](ctx_lib.get_libctx(), fn, nil)
fn:free()
table.sort(ret)
return ret
end
function _M.list_cipher_algorithms()
if BORINGSSL then
return nil, "openssl.list_cipher_algorithms is not supported on BoringSSL"
end
require "resty.openssl.include.evp.cipher"
local ret = list_legacy("EVP_CIPHER",
OPENSSL_3X and C.EVP_CIPHER_get_nid or C.EVP_CIPHER_nid)
if OPENSSL_3X then
local ret_provided = list_provided("EVP_CIPHER")
for _, r in ipairs(ret_provided) do
table.insert(ret, r)
end
end
return ret
end
function _M.list_digest_algorithms()
if BORINGSSL then
return nil, "openssl.list_digest_algorithms is not supported on BoringSSL"
end
require "resty.openssl.include.evp.md"
local ret = list_legacy("EVP_MD",
OPENSSL_3X and C.EVP_MD_get_type or C.EVP_MD_type)
if OPENSSL_3X then
local ret_provided = list_provided("EVP_MD")
for _, r in ipairs(ret_provided) do
table.insert(ret, r)
end
end
return ret
end
function _M.list_mac_algorithms()
if not OPENSSL_3X then
return nil, "openssl.list_mac_algorithms is only supported from OpenSSL 3.0"
end
return list_provided("EVP_MAC")
end
function _M.list_kdf_algorithms()
if not OPENSSL_3X then
return nil, "openssl.list_kdf_algorithms is only supported from OpenSSL 3.0"
end
return list_provided("EVP_KDF")
end
local valid_ssl_protocols = {
["SSLv3"] = 0x0300,
["TLSv1"] = 0x0301,
["TLSv1.1"] = 0x0302,
["TLSv1.2"] = 0x0303,
["TLSv1.3"] = 0x0304,
}
function _M.list_ssl_ciphers(cipher_list, ciphersuites, protocol)
local ssl_lib = require("resty.openssl.ssl")
local ssl_macro = require("resty.openssl.include.ssl")
if protocol then
if not valid_ssl_protocols[protocol] then
return nil, "unknown protocol \"" .. protocol .. "\""
end
protocol = valid_ssl_protocols[protocol]
end
local ssl_ctx = C.SSL_CTX_new(C.TLS_server_method())
if ssl_ctx == nil then
return nil, format_error("SSL_CTX_new")
end
ffi.gc(ssl_ctx, C.SSL_CTX_free)
local ssl = C.SSL_new(ssl_ctx)
if ssl == nil then
return nil, format_error("SSL_new")
end
ffi.gc(ssl, C.SSL_free)
if protocol then
if ssl_macro.SSL_set_min_proto_version(ssl, protocol) == 0 or
ssl_macro.SSL_set_max_proto_version(ssl, protocol) == 0 then
return nil, format_error("SSL_set_min/max_proto_version")
end
end
ssl = { ctx = ssl }
local ok, err
if cipher_list then
ok, err = ssl_lib.set_cipher_list(ssl, cipher_list)
if not ok then
return nil, err
end
end
if ciphersuites then
ok, err = ssl_lib.set_ciphersuites(ssl, ciphersuites)
if not ok then
return nil, err
end
end
return ssl_lib.get_ciphers(ssl)
end
return _M

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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
}

View File

@ -0,0 +1,143 @@
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_gc = ffi.gc
local ffi_str = ffi.string
local C = ffi.C
require "resty.openssl.include.ecdsa"
local bn_lib = require "resty.openssl.bn"
local format_error = require("resty.openssl.err").format_error
local ceil = math.ceil
local _M = {}
--[[ A DER formatted ECDSA signature looks like
SEQUENCE {
INTEGER
4B 5F CF E8 A7 BD 6A C2 1D 25 0D F8 DE 9C EF DC
C4 DF 33 F3 AF 2F 3D 5B 83 2C 1F BD 98 C8 61 34
INTEGER
7E F9 E9 60 B1 E6 7F 59 9E 2C 38 22 39 B2 C4 B1
71 3E FA AE 24 A4 B7 D2 03 5A 60 8D F3 34 3D E8
}
It has ASN.1 headers on both the SEQUENCE and INTEGERs, so
the total length is typically 70 bytes (3 headers, 2 bytes each).
The binary form is typically 64 bytes.
]]
local function group_size(ec_key)
local group = C.EC_KEY_get0_group(ec_key)
if group == nil then
assert("failed to get EC group", 2)
end
local sz = C.EC_GROUP_order_bits(group)
if sz <= 0 then
assert("failed to get EC group order bits", 2)
end
return ceil(sz / 8)
end
_M.sig_der2raw = function(der, ec_key)
if ec_key == nil then
error("ec_key is required", 2)
end
local psize = group_size(ec_key)
local buf = ffi.new("const unsigned char*", der)
local buf_ptr = ffi.new("const unsigned char*[1]", buf)
local sig = C.d2i_ECDSA_SIG(nil, buf_ptr, #der)
if sig == nil then
return nil, format_error("failed to parse ECDSA signature: d2i_ECDSA_SIG")
end
ffi_gc(sig, C.ECDSA_SIG_free)
local bn_r_ptr = ffi_new("const BIGNUM*[1]")
local bn_s_ptr = ffi_new("const BIGNUM*[1]")
C.ECDSA_SIG_get0(sig, bn_r_ptr, bn_s_ptr)
if bn_r_ptr[0] == nil or bn_s_ptr[0] == nil then
return nil, format_error("failed to get r or s from sig")
end
local bn_r = bn_lib.dup(bn_r_ptr[0])
local bn_s = bn_lib.dup(bn_s_ptr[0])
if bn_r == nil or bn_s == nil then
return nil, format_error("failed to dup r or s")
end
local rbin, err = bn_r:to_binary(psize)
if err then
return nil, "failed to parse r to binary: " .. err
end
local sbin, err = bn_s:to_binary(psize)
if err then
return nil, "failed to parse s to binary: " .. err
end
return rbin .. sbin
end
_M.sig_raw2der = function(bin, ec_key)
if ec_key == nil then
error("ec_key is required", 2)
end
local psize = group_size(ec_key)
if #bin ~= psize * 2 then
return nil, "invalid signature length, expect " .. (psize * 2) .. " but got " .. #bin
end
local rbin = string.sub(bin, 1, psize)
local sbin = string.sub(bin, psize + 1)
local bn_r, err = bn_lib.from_binary(rbin)
if err then
return nil, "failed to parse r from binary: " .. err
end
local bn_s, err = bn_lib.from_binary(sbin)
if err then
return nil, "failed to parse s from binary: " .. err
end
local sig = C.ECDSA_SIG_new()
if sig == nil then
return nil, format_error("ECDSA_SIG_new")
end
ffi_gc(sig, C.ECDSA_SIG_free)
local bn_r0 = C.BN_dup(bn_r.ctx)
local bn_s0 = C.BN_dup(bn_s.ctx)
if not bn_r0 or not bn_s0 then
return nil, format_error("failed to BN_dup r or s")
end
local ok = C.ECDSA_SIG_set0(sig, bn_r0, bn_s0)
if ok ~= 1 then
return nil, format_error("failed to set r and s to sig")
end
local der_len = C.i2d_ECDSA_SIG(sig, nil)
if der_len <= 0 then
return nil, format_error("failed to get ECDSA signature size")
end
local buf = ffi_new("unsigned char[?]", der_len)
local buf_ptr = ffi.new("unsigned char*[1]", buf)
der_len = C.i2d_ECDSA_SIG(sig, buf_ptr)
if der_len <= 0 then
return nil, format_error("failed to encode ECDSA signature to DER")
end
return ffi_str(buf, der_len)
end
return _M

View File

@ -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

View File

@ -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,
}

View File

@ -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,
}

View File

@ -0,0 +1,435 @@
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(pad)
if pad then
if type(pad) ~= "number" then
return nil, "bn:to_binary: expect a number at #1"
elseif OPENSSL_10 then
return nil, "bn:to_binary: padding is only supported on OpenSSL 1.1.0 or later"
end
end
local length
if not pad then
length = (C.BN_num_bits(self.ctx)+7)/8
-- align to bytes
length = floor(length)
else
length = pad
end
local buf = ctypes.uchar_array(length)
local sz
if not pad then
sz = C.BN_bn2bin(self.ctx, buf)
else
sz = C.BN_bn2binpad(self.ctx, buf, pad)
end
if sz <= 0 then
return nil, format_error("bn:to_binary")
end
return ffi_str(buf, sz)
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

View File

@ -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

View File

@ -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,
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
}

View File

@ -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

View File

@ -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,
}

View File

@ -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);
]]

View File

@ -0,0 +1,79 @@
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_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
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

View File

@ -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);
]]

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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);
]]

View File

@ -0,0 +1,16 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
ECDSA_SIG *ECDSA_SIG_new(void);
void ECDSA_SIG_free(ECDSA_SIG *sig);
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
int i2d_ECDSA_SIG(const ECDSA_SIG *sig, unsigned char **pp);
ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **sig, const unsigned char **pp, long len);
int EC_GROUP_order_bits(const EC_GROUP *group);
]]

View File

@ -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);
]]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
]]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
]]

Some files were not shown because too many files have changed in this diff Show More