summaryrefslogtreecommitdiffstats
path: root/server/resty/openssl/x509
diff options
context:
space:
mode:
Diffstat (limited to 'server/resty/openssl/x509')
-rw-r--r--server/resty/openssl/x509/altname.lua248
-rw-r--r--server/resty/openssl/x509/chain.lua76
-rw-r--r--server/resty/openssl/x509/crl.lua607
-rw-r--r--server/resty/openssl/x509/csr.lua531
-rw-r--r--server/resty/openssl/x509/extension.lua281
-rw-r--r--server/resty/openssl/x509/extension/dist_points.lua75
-rw-r--r--server/resty/openssl/x509/extension/info_access.lua137
-rw-r--r--server/resty/openssl/x509/extensions.lua84
-rw-r--r--server/resty/openssl/x509/init.lua1071
-rw-r--r--server/resty/openssl/x509/name.lua156
-rw-r--r--server/resty/openssl/x509/revoked.lua108
-rw-r--r--server/resty/openssl/x509/store.lua227
12 files changed, 3601 insertions, 0 deletions
diff --git a/server/resty/openssl/x509/altname.lua b/server/resty/openssl/x509/altname.lua
new file mode 100644
index 0000000..34bf9e0
--- /dev/null
+++ b/server/resty/openssl/x509/altname.lua
@@ -0,0 +1,248 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+local asn1_macro = require "resty.openssl.include.asn1"
+local stack_lib = require "resty.openssl.stack"
+local name_lib = require "resty.openssl.x509.name"
+local altname_macro = require "resty.openssl.include.x509.altname"
+
+local _M = {}
+
+local general_names_ptr_ct = ffi.typeof("GENERAL_NAMES*")
+
+local STACK = "GENERAL_NAME"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+local types = altname_macro.types
+
+local AF_INET = 2
+local AF_INET6 = 10
+if ffi.os == "OSX" then
+ AF_INET6 = 30
+elseif ffi.os == "BSD" then
+ AF_INET6 = 28
+elseif ffi.os == "Windows" then
+ AF_INET6 = 23
+end
+
+ffi.cdef [[
+ typedef int socklen_t;
+ int inet_pton(int af, const char *restrict src, void *restrict dst);
+ const char *inet_ntop(int af, const void *restrict src,
+ char *restrict dst, socklen_t size);
+]]
+
+local ip_buffer = ffi.new("unsigned char [46]") -- 46 bytes enough for both string ipv6 and binary ipv6
+
+-- similar to GENERAL_NAME_print, but returns value instead of print
+local gn_decode = function(ctx)
+ local typ = ctx.type
+ local k = altname_macro.literals[typ]
+ local v
+ if typ == types.OtherName then
+ v = "OtherName:<unsupported>"
+ elseif typ == types.RFC822Name then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.rfc822Name))
+ elseif typ == types.DNS then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.dNSName))
+ elseif typ == types.X400 then
+ v = "X400:<unsupported>"
+ elseif typ == types.DirName then
+ v = name_lib.dup(ctx.d.directoryName)
+ elseif typ == types.EdiParty then
+ v = "EdiParty:<unsupported>"
+ elseif typ == types.URI then
+ v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier))
+ elseif typ == types.IP then
+ v = asn1_macro.ASN1_STRING_get0_data(ctx.d.iPAddress)
+ local l = tonumber(C.ASN1_STRING_length(ctx.d.iPAddress))
+ if l ~= 4 and l ~= 16 then
+ error("Unknown IP address type")
+ end
+ v = C.inet_ntop(l == 4 and AF_INET or AF_INET6, v, ip_buffer, 46)
+ v = ffi_str(v)
+ elseif typ == types.RID then
+ v = "RID:<unsupported>"
+ else
+ error("unknown type" .. typ .. "-> " .. types.OtherName)
+ end
+ return { k, v }
+end
+
+-- shared with info_access
+_M.gn_decode = gn_decode
+
+local mt = stack_lib.mt_of(STACK, gn_decode, _M)
+local mt__pairs = mt.__pairs
+mt.__pairs = function(tbl)
+ local f = mt__pairs(tbl)
+ return function()
+ local _, e = f()
+ if not e then return end
+ return unpack(e)
+ end
+end
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "x509.altname.new: OPENSSL_sk_new_null() failed"
+ end
+ local cast = ffi_cast("GENERAL_NAMES*", ctx)
+
+ local self = setmetatable({
+ ctx = ctx,
+ cast = cast,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(general_names_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(general_names_ptr_ct, ctx) then
+ return nil, "x509.altname.dup: expect a GENERAL_NAMES* ctx at #1"
+ end
+
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ cast = ffi_cast("GENERAL_NAMES*", dup_ctx),
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+local function gn_set(gn, typ, value)
+ if type(typ) ~= 'string' then
+ return "x509.altname:gn_set: expect a string at #1"
+ end
+ local typ_lower = typ:lower()
+ if type(value) ~= 'string' then
+ return "x509.altname:gn_set: except a string at #2"
+ end
+
+ local txt = value
+ local gn_type = types[typ_lower]
+
+ if not gn_type then
+ return "x509.altname:gn_set: unknown type " .. typ
+ end
+
+ if gn_type == types.IP then
+ if C.inet_pton(AF_INET, txt, ip_buffer) == 1 then
+ txt = ffi_str(ip_buffer, 4)
+ elseif C.inet_pton(AF_INET6, txt, ip_buffer) == 1 then
+ txt = ffi_str(ip_buffer, 16)
+ else
+ return "x509.altname:gn_set: invalid IP address " .. txt
+ end
+
+ elseif gn_type ~= types.Email and
+ gn_type ~= types.URI and
+ gn_type ~= types.DNS then
+ return "x509.altname:gn_set: setting type " .. typ .. " is currently not supported"
+ end
+
+ gn.type = gn_type
+
+ local asn1_string = C.ASN1_IA5STRING_new()
+ if asn1_string == nil then
+ return "x509.altname:gn_set: ASN1_STRING_type_new() failed"
+ end
+
+ local code = C.ASN1_STRING_set(asn1_string, txt, #txt)
+ if code ~= 1 then
+ C.ASN1_STRING_free(asn1_string)
+ return "x509.altname:gn_set: ASN1_STRING_set() failed: " .. code
+ end
+ gn.d.ia5 = asn1_string
+end
+
+-- shared with info_access
+_M.gn_set = gn_set
+
+function _M:add(typ, value)
+
+ -- the stack element stays with stack
+ -- we shouldn't add gc handler if it's already been
+ -- pushed to stack. instead, rely on the gc handler
+ -- of the stack to release all memories
+ local gn = C.GENERAL_NAME_new()
+ if gn == nil then
+ return nil, "x509.altname:add: GENERAL_NAME_new() failed"
+ end
+
+ local err = gn_set(gn, typ, value)
+ if err then
+ C.GENERAL_NAME_free(gn)
+ return nil, err
+ end
+
+ local _, err = add(self.ctx, gn)
+ if err then
+ C.GENERAL_NAME_free(gn)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(gn, C.GENERAL_NAME_free)
+ self._elem_refs[self._elem_refs_idx] = gn
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+ return self
+end
+
+_M.all = function(self)
+ local ret = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ ret[k] = v
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__pairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+mt.__tostring = function(self)
+ local values = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ table.insert(values, k .. "=" .. v)
+ else
+ break
+ end
+ end
+ table.sort(values)
+ return table.concat(values, "/")
+end
+
+_M.tostring = mt.__tostring
+
+return _M
diff --git a/server/resty/openssl/x509/chain.lua b/server/resty/openssl/x509/chain.lua
new file mode 100644
index 0000000..5557ea0
--- /dev/null
+++ b/server/resty/openssl/x509/chain.lua
@@ -0,0 +1,76 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+local stack_lib = require "resty.openssl.stack"
+local x509_lib = require "resty.openssl.x509"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "X509"
+local gc = stack_lib.gc_of(STACK)
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local mt = stack_lib.mt_of(STACK, x509_lib.dup, _M)
+
+function _M.new()
+ local raw = new()
+
+ local self = setmetatable({
+ stack_of = STACK,
+ ctx = raw,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
+ and l.stack_of and l.stack_of == STACK
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "x509.chain.dup: expect a stack ctx at #1, got " .. type(ctx)
+ end
+ -- sk_X509_dup plus up ref for each X509 element
+ local ctx = C.X509_chain_up_ref(ctx)
+ if ctx == nil then
+ return nil, "x509.chain.dup: X509_chain_up_ref() failed"
+ end
+ ffi_gc(ctx, gc)
+
+ return setmetatable({
+ stack_of = STACK,
+ ctx = ctx,
+ }, mt)
+end
+
+function _M:add(x509)
+ if not x509_lib.istype(x509) then
+ return nil, "x509.chain:add: expect a x509 instance at #1"
+ end
+
+ local dup = C.X509_dup(x509.ctx)
+ if dup == nil then
+ return nil, format_error("x509.chain:add: X509_dup")
+ end
+
+ local _, err = add(self.ctx, dup)
+ if err then
+ C.X509_free(dup)
+ return nil, err
+ end
+
+ return true
+end
+
+_M.all = stack_lib.all_func(mt)
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/crl.lua b/server/resty/openssl/x509/crl.lua
new file mode 100644
index 0000000..3ee4501
--- /dev/null
+++ b/server/resty/openssl/x509/crl.lua
@@ -0,0 +1,607 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.x509.crl"
+require "resty.openssl.include.pem"
+require "resty.openssl.include.x509v3"
+local asn1_lib = require("resty.openssl.asn1")
+local bn_lib = require("resty.openssl.bn")
+local revoked_lib = require("resty.openssl.x509.revoked")
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local pkey_lib = require("resty.openssl.pkey")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local ctx_lib = require "resty.openssl.ctx"
+local stack_lib = require "resty.openssl.stack"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+local accessors = {}
+
+accessors.set_issuer_name = C.X509_CRL_set_issuer_name
+accessors.set_version = C.X509_CRL_set_version
+
+
+if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
+ accessors.get_last_update = C.X509_CRL_get0_lastUpdate
+ accessors.set_last_update = C.X509_CRL_set1_lastUpdate
+ accessors.get_next_update = C.X509_CRL_get0_nextUpdate
+ accessors.set_next_update = C.X509_CRL_set1_nextUpdate
+ accessors.get_version = C.X509_CRL_get_version
+ accessors.get_issuer_name = C.X509_CRL_get_issuer -- returns internal ptr
+ accessors.get_signature_nid = C.X509_CRL_get_signature_nid
+ -- BORINGSSL_110 exports X509_CRL_get_signature_nid, but just ignored for simplicity
+ accessors.get_revoked = C.X509_CRL_get_REVOKED
+elseif OPENSSL_10 or BORINGSSL_110 then
+ accessors.get_last_update = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.lastUpdate
+ end
+ accessors.set_last_update = C.X509_CRL_set_lastUpdate
+ accessors.get_next_update = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.nextUpdate
+ end
+ accessors.set_next_update = C.X509_CRL_set_nextUpdate
+ accessors.get_version = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(crl.crl.version)
+ end
+ accessors.get_issuer_name = function(crl)
+ if crl == nil or crl.crl == nil then
+ return nil
+ end
+ return crl.crl.issuer
+ end
+ accessors.get_signature_nid = function(crl)
+ if crl == nil or crl.crl == nil or crl.crl.sig_alg == nil then
+ return nil
+ end
+ return C.OBJ_obj2nid(crl.crl.sig_alg.algorithm)
+ end
+ accessors.get_revoked = function(crl)
+ return crl.crl.revoked
+ end
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509_CRL, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_CRL_bio, self.ctx)
+ else
+ return nil, "x509.crl:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+local x509_crl_ptr_ct = ffi.typeof("X509_CRL*")
+
+function _M.new(crl, fmt, properties)
+ local ctx
+ if not crl then
+ if OPENSSL_3X then
+ ctx = C.X509_CRL_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_CRL_new()
+ end
+ if ctx == nil then
+ return nil, "x509.crl.new: X509_CRL_new() failed"
+ end
+ elseif type(crl) == "string" then
+ -- routine for load an existing csr
+ local bio = C.BIO_new_mem_buf(crl, #crl)
+ if bio == nil then
+ return nil, format_error("x509.crl.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509_CRL(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "x509.crl.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_CRL_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.crl.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ else
+ return nil, "x509.crl.new: expect nil or a string at #1"
+ end
+ ffi_gc(ctx, C.X509_CRL_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l and l.ctx and ffi.istype(x509_crl_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_crl_ptr_ct, ctx) then
+ return nil, "x509.crl.dup: expect a x509.crl ctx at #1"
+ end
+ local ctx = C.X509_CRL_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.crl.dup: X509_CRL_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_CRL_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:text()
+ return bio_util.read_wrap(C.X509_CRL_print, self.ctx)
+end
+
+local function revoked_decode(ctx)
+ if OPENSSL_10 then
+ error("x509.crl:revoked_decode: not supported on OpenSSL 1.0")
+ end
+
+ local ret = {}
+ local serial = C.X509_REVOKED_get0_serialNumber(ctx)
+ if serial ~= nil then
+ serial = C.ASN1_INTEGER_to_BN(serial, nil)
+ if serial == nil then
+ error("x509.crl:revoked_decode: ASN1_INTEGER_to_BN() failed")
+ end
+ ffi_gc(serial, C.BN_free)
+ ret["serial_number"] = bn_lib.to_hex({ctx = serial})
+ end
+
+ local date = C.X509_REVOKED_get0_revocationDate(ctx)
+ if date ~= nil then
+ date = asn1_lib.asn1_to_unix(date)
+ ret["revocation_date"] = date
+ end
+
+ return ret
+end
+
+local revoked_mt = stack_lib.mt_of("X509_REVOKED", revoked_decode, _M)
+
+local function nil_iter() return nil end
+local function revoked_iter(self)
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return nil_iter
+ end
+
+ return revoked_mt.__ipairs({ctx = stack})
+end
+
+mt.__pairs = revoked_iter
+mt.__ipairs = revoked_iter
+mt.__index = function(self, k)
+ local i = tonumber(k)
+ if not i then
+ return _M[k]
+ end
+
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return nil
+ end
+
+ return revoked_mt.__index({ctx = stack}, i)
+end
+mt.__len = function(self)
+ local stack = accessors.get_revoked(self.ctx)
+ if stack == nil then
+ return 0
+ end
+
+ return revoked_mt.__len({ctx = stack})
+end
+
+_M.all = function(self)
+ local ret = {}
+ local _next = mt.__pairs(self)
+ while true do
+ local k, v = _next()
+ if k then
+ ret[k] = v
+ else
+ break
+ end
+ end
+ return ret
+end
+_M.each = mt.__pairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+--- Adds revoked item to stack of revoked certificates of crl
+-- @tparam table Instance of crl module
+-- @tparam table Instance of revoked module
+-- @treturn boolean true if revoked item was successfully added or false otherwise
+-- @treturn[opt] string Returns optional error message in case of error
+function _M:add_revoked(revoked)
+ if not revoked_lib.istype(revoked) then
+ return false, "x509.crl:add_revoked: expect a revoked instance at #1"
+ end
+ local ctx = C.X509_REVOKED_dup(revoked.ctx)
+ if ctx == nil then
+ return nil, "x509.crl:add_revoked: X509_REVOKED_dup() failed"
+ end
+
+ if C.X509_CRL_add0_revoked(self.ctx, ctx) == 0 then
+ return false, format_error("x509.crl:add_revoked")
+ end
+
+ return true
+end
+
+local ptr_ptr_of_x509_revoked = ffi.typeof("X509_REVOKED*[1]")
+function _M:get_by_serial(sn)
+ local bn, err
+ if bn_lib.istype(sn) then
+ bn = sn
+ elseif type(sn) == "string" then
+ bn, err = bn_lib.from_hex(sn)
+ if err then
+ return nil, "x509.crl:find: can't decode bn: " .. err
+ end
+ else
+ return nil, "x509.crl:find: expect a bn instance at #1"
+ end
+
+ local sn_asn1 = C.BN_to_ASN1_INTEGER(bn.ctx, nil)
+ if sn_asn1 == nil then
+ return nil, "x509.crl:find: BN_to_ASN1_INTEGER() failed"
+ end
+ ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
+
+ local pp = ptr_ptr_of_x509_revoked()
+ local code = C.X509_CRL_get0_by_serial(self.ctx, pp, sn_asn1)
+ if code == 1 then
+ return revoked_decode(pp[0])
+ elseif code == 2 then
+ return nil, "not revoked (removeFromCRL)"
+ end
+
+ -- 0 or other
+ return nil
+end
+
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.crl:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509.crl:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509.crl:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_CRL_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509.crl:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.crl:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_CRL_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509.crl:verify", code)
+ end
+end
+
+-- AUTO GENERATED
+local function get_extension(ctx, nid_txt, last_pos)
+ last_pos = (last_pos or 0) - 1
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+ local pos = C.X509_CRL_get_ext_by_NID(ctx, nid, last_pos)
+ if pos == -1 then
+ return nil
+ end
+ local ctx = C.X509_CRL_get_ext(ctx, pos)
+ if ctx == nil then
+ return nil, nil, format_error()
+ end
+ return ctx, pos
+end
+
+-- AUTO GENERATED
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509.crl:add_extension: expect a x509.extension instance at #1"
+ end
+
+ -- X509_CRL_add_ext returnes the stack on success, and NULL on error
+ -- the X509_EXTENSION ctx is dupped internally
+ if C.X509_CRL_add_ext(self.ctx, extension.ctx, -1) == nil then
+ return false, format_error("x509.crl:add_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509.crl:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509.crl:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local X509_CRL_delete_ext
+if OPENSSL_11_OR_LATER then
+ X509_CRL_delete_ext = C.X509_CRL_delete_ext
+elseif OPENSSL_10 then
+ X509_CRL_delete_ext = function(ctx, pos)
+ return C.X509v3_delete_ext(ctx.crl.extensions, pos)
+ end
+else
+ X509_CRL_delete_ext = function(...)
+ error("X509_CRL_delete_ext undefined")
+ end
+end
+
+-- AUTO GENERATED
+function _M:set_extension(extension, last_pos)
+ if not extension_lib.istype(extension) then
+ return false, "x509.crl:set_extension: expect a x509.extension instance at #1"
+ end
+
+ last_pos = (last_pos or 0) - 1
+
+ local nid = extension:get_object().nid
+ local pos = C.X509_CRL_get_ext_by_NID(self.ctx, nid, last_pos)
+ -- pos may be -1, which means not found, it's fine, we will add new one instead of replace
+
+ local removed = X509_CRL_delete_ext(self.ctx, pos)
+ C.X509_EXTENSION_free(removed)
+
+ if C.X509_CRL_add_ext(self.ctx, extension.ctx, pos) == nil then
+ return false, format_error("x509.crl:set_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.crl:set_extension_critical: " .. err
+ end
+
+ if C.X509_EXTENSION_set_critical(ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509.crl:set_extension_critical")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.crl:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- AUTO GENERATED
+function _M:get_issuer_name()
+ local got = accessors.get_issuer_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_issuer_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.crl:set_issuer_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_issuer_name(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_issuer_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_last_update()
+ local got = accessors.get_last_update(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_last_update(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_last_update: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_last_update(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_last_update")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_next_update()
+ local got = accessors.get_next_update(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_next_update(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_next_update: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_next_update(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_next_update")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.crl:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509.crl:set_version")
+ end
+ return true
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.crl:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
+
diff --git a/server/resty/openssl/x509/csr.lua b/server/resty/openssl/x509/csr.lua
new file mode 100644
index 0000000..08c4860
--- /dev/null
+++ b/server/resty/openssl/x509/csr.lua
@@ -0,0 +1,531 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.pem"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.x509.csr"
+require "resty.openssl.include.asn1"
+local stack_macro = require "resty.openssl.include.stack"
+local stack_lib = require "resty.openssl.stack"
+local pkey_lib = require "resty.openssl.pkey"
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local extensions_lib = require("resty.openssl.x509.extensions")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+local accessors = {}
+
+accessors.set_subject_name = C.X509_REQ_set_subject_name
+accessors.get_pubkey = C.X509_REQ_get_pubkey
+accessors.set_pubkey = C.X509_REQ_set_pubkey
+accessors.set_version = C.X509_REQ_set_version
+
+if OPENSSL_11_OR_LATER or BORINGSSL_110 then
+ accessors.get_signature_nid = C.X509_REQ_get_signature_nid
+elseif OPENSSL_10 then
+ accessors.get_signature_nid = function(csr)
+ if csr == nil or csr.sig_alg == nil then
+ return nil
+ end
+ return C.OBJ_obj2nid(csr.sig_alg.algorithm)
+ end
+end
+
+if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
+ accessors.get_subject_name = C.X509_REQ_get_subject_name -- returns internal ptr
+ accessors.get_version = C.X509_REQ_get_version
+elseif OPENSSL_10 or BORINGSSL_110 then
+ accessors.get_subject_name = function(csr)
+ if csr == nil or csr.req_info == nil then
+ return nil
+ end
+ return csr.req_info.subject
+ end
+ accessors.get_version = function(csr)
+ if csr == nil or csr.req_info == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(csr.req_info.version)
+ end
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509_REQ, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_REQ_bio, self.ctx)
+ else
+ return nil, "x509.csr:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+local x509_req_ptr_ct = ffi.typeof("X509_REQ*")
+
+local stack_ptr_type = ffi.typeof("struct stack_st *[1]")
+local x509_extensions_gc = stack_lib.gc_of("X509_EXTENSION")
+
+function _M.new(csr, fmt, properties)
+ local ctx
+ if not csr then
+ if OPENSSL_3X then
+ ctx = C.X509_REQ_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_REQ_new()
+ end
+ if ctx == nil then
+ return nil, "x509.csr.new: X509_REQ_new() failed"
+ end
+ elseif type(csr) == "string" then
+ -- routine for load an existing csr
+ local bio = C.BIO_new_mem_buf(csr, #csr)
+ if bio == nil then
+ return nil, format_error("x509.csr.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509_REQ(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ return nil, "x509.csr.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_REQ_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.csr.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ else
+ return nil, "x509.csr.new: expect nil or a string at #1"
+ end
+ ffi_gc(ctx, C.X509_REQ_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l and l.ctx and ffi.istype(x509_req_ptr_ct, l.ctx)
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:check_private_key(key)
+ if not pkey_lib.istype(key) then
+ return false, "x509.csr:check_private_key: except a pkey instance at #1"
+ end
+
+ if not key:is_private() then
+ return false, "x509.csr:check_private_key: not a private key"
+ end
+
+ if C.X509_REQ_check_private_key(self.ctx, key.ctx) == 1 then
+ return true
+ end
+ return false, format_error("x509.csr:check_private_key")
+end
+
+--- Get all csr extensions
+-- @tparam table self Instance of csr
+-- @treturn Extensions object
+function _M:get_extensions()
+ local extensions = C.X509_REQ_get_extensions(self.ctx)
+ -- GC handler is sk_X509_EXTENSION_pop_free
+ ffi_gc(extensions, x509_extensions_gc)
+
+ return extensions_lib.dup(extensions)
+end
+
+local function get_extension(ctx, nid_txt, last_pos)
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+
+ local extensions = C.X509_REQ_get_extensions(ctx)
+ if extensions == nil then
+ return nil, nil, format_error("csr.get_extension: X509_REQ_get_extensions")
+ end
+ ffi_gc(extensions, x509_extensions_gc)
+
+ -- make 1-index array to 0-index
+ last_pos = (last_pos or 0) -1
+ local ext_idx = C.X509v3_get_ext_by_NID(extensions, nid, last_pos)
+ if ext_idx == -1 then
+ err = ("X509v3_get_ext_by_NID extension for %d not found"):format(nid)
+ return nil, -1, format_error(err)
+ end
+
+ local ctx = C.X509v3_get_ext(extensions, ext_idx)
+ if ctx == nil then
+ return nil, nil, format_error("X509v3_get_ext")
+ end
+
+ return ctx, ext_idx, nil
+end
+
+--- Get a csr extension
+-- @tparam table self Instance of csr
+-- @tparam string|number Nid number or name of the extension
+-- @tparam number Position to start looking for the extension; default to look from start if omitted
+-- @treturn Parsed extension object or nil if not found
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509.csr:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509.csr:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local function modify_extension(replace, ctx, nid, toset, crit)
+ local extensions_ptr = stack_ptr_type()
+ extensions_ptr[0] = C.X509_REQ_get_extensions(ctx)
+ local need_cleanup = extensions_ptr[0] ~= nil and
+ -- extensions_ptr being nil is fine: it may just because there's no extension yet
+ -- https://github.com/openssl/openssl/commit/2039ac07b401932fa30a05ade80b3626e189d78a
+ -- introduces a change that a empty stack instead of NULL will be returned in no extension
+ -- is found. so we need to double check the number if it's not NULL.
+ stack_macro.OPENSSL_sk_num(extensions_ptr[0]) > 0
+
+ local flag
+ if replace then
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ flag = 0x2
+ else
+ -- x509v3.h: # define X509V3_ADD_APPEND 1L
+ flag = 0x1
+ end
+
+ local code = C.X509V3_add1_i2d(extensions_ptr, nid, toset, crit and 1 or 0, flag)
+ -- when the stack is newly allocated, we want to cleanup the newly created stack as well
+ -- setting the gc handler here as it's mutated in X509V3_add1_i2d if it's pointing to NULL
+ ffi_gc(extensions_ptr[0], x509_extensions_gc)
+ if code ~= 1 then
+ return false, format_error("X509V3_add1_i2d", code)
+ end
+
+ code = C.X509_REQ_add_extensions(ctx, extensions_ptr[0])
+ if code ~= 1 then
+ return false, format_error("X509_REQ_add_extensions", code)
+ end
+
+ if need_cleanup then
+ -- cleanup old attributes
+ -- delete the first only, why?
+ local attr = C.X509_REQ_delete_attr(ctx, 0)
+ if attr ~= nil then
+ C.X509_ATTRIBUTE_free(attr)
+ end
+ end
+
+ -- mark encoded form as invalid so next time it will be re-encoded
+ if OPENSSL_11_OR_LATER then
+ C.i2d_re_X509_REQ_tbs(ctx, nil)
+ else
+ ctx.req_info.enc.modified = 1
+ end
+
+ return true
+end
+
+local function add_extension(...)
+ return modify_extension(false, ...)
+end
+
+local function replace_extension(...)
+ return modify_extension(true, ...)
+end
+
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ local nid = extension:get_object().nid
+ local toset = extension_lib.to_data(extension, nid)
+ return add_extension(self.ctx, nid, toset.ctx, extension:get_critical())
+end
+
+function _M:set_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ local nid = extension:get_object().nid
+ local toset = extension_lib.to_data(extension, nid)
+ return replace_extension(self.ctx, nid, toset.ctx, extension:get_critical())
+end
+
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, "x509.csr:set_extension_critical: " .. err
+ end
+
+ local extension, _, err = get_extension(self.ctx, nid, last_pos)
+ if err then
+ return nil, "x509.csr:set_extension_critical: " .. err
+ end
+
+ local toset = extension_lib.to_data({
+ ctx = extension
+ }, nid)
+ return replace_extension(self.ctx, nid, toset.ctx, crit and 1 or 0)
+end
+
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509.csr:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.csr:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509.csr:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509.csr:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_REQ_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509.csr:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509.csr:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_REQ_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509.csr:verify", code)
+ end
+end
+-- AUTO GENERATED
+function _M:get_subject_name()
+ local got = accessors.get_subject_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_subject_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_subject_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_subject_name(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_subject_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_pubkey()
+ local got = accessors.get_pubkey(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.pkey")
+ -- returned a copied instance directly
+ return lib.new(got)
+end
+
+-- AUTO GENERATED
+function _M:set_pubkey(toset)
+ local lib = require("resty.openssl.pkey")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_pubkey: expect a pkey instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_pubkey(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_pubkey")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509.csr:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509.csr:set_version")
+ end
+ return true
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name()
+ local crit = ctypes.ptr_of_int()
+ local extensions = C.X509_REQ_get_extensions(self.ctx)
+ -- GC handler is sk_X509_EXTENSION_pop_free
+ ffi_gc(extensions, x509_extensions_gc)
+ local got = C.X509V3_get_d2i(extensions, NID_subject_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509.csr:get_subject_alt_name: extension of subject_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509.csr:get_subject_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509.csr:set_subject_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ return replace_extension(self.ctx, NID_subject_alt_name, toset)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_subject_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name_critical()
+ return _M.get_extension_critical(self, NID_subject_alt_name)
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509.csr:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
+
diff --git a/server/resty/openssl/x509/extension.lua b/server/resty/openssl/x509/extension.lua
new file mode 100644
index 0000000..ca23158
--- /dev/null
+++ b/server/resty/openssl/x509/extension.lua
@@ -0,0 +1,281 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_new = ffi.new
+local ffi_cast = ffi.cast
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509.extension"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.bio"
+require "resty.openssl.include.conf"
+local asn1_macro = require("resty.openssl.include.asn1")
+local objects_lib = require "resty.openssl.objects"
+local stack_lib = require("resty.openssl.stack")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local format_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+local BORINGSSL = require("resty.openssl.version").BORINGSSL
+
+local _M = {}
+local mt = { __index = _M }
+
+local x509_extension_ptr_ct = ffi.typeof("X509_EXTENSION*")
+
+local extension_types = {
+ issuer = "resty.openssl.x509",
+ subject = "resty.openssl.x509",
+ request = "resty.openssl.x509.csr",
+ crl = "resty.openssl.x509.crl",
+}
+
+if OPENSSL_3X then
+ extension_types["issuer_pkey"] = "resty.openssl.pkey"
+end
+
+local nconf_load
+if BORINGSSL then
+ nconf_load = function()
+ return nil, "NCONF_load_bio not exported in BoringSSL"
+ end
+else
+ nconf_load = function(conf, str)
+ local bio = C.BIO_new_mem_buf(str, #str)
+ if bio == nil then
+ return format_error("BIO_new_mem_buf")
+ end
+ ffi_gc(bio, C.BIO_free)
+
+ if C.NCONF_load_bio(conf, bio, nil) ~= 1 then
+ return format_error("NCONF_load_bio")
+ end
+ end
+end
+
+function _M.new(txtnid, value, data)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ if type(value) ~= 'string' then
+ return nil, "x509.extension.new: expect string at #2"
+ end
+ -- get a ptr and also zerofill the struct
+ local x509_ctx_ptr = ffi_new('X509V3_CTX[1]')
+
+ local conf = C.NCONF_new(nil)
+ if conf == nil then
+ return nil, format_error("NCONF_new")
+ end
+ ffi_gc(conf, C.NCONF_free)
+
+ if type(data) == 'table' then
+ local args = {}
+ if data.db then
+ if type(data.db) ~= 'string' then
+ return nil, "x509.extension.new: expect data.db must be a string"
+ end
+ err = nconf_load(conf, data)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ end
+
+ for k, t in pairs(extension_types) do
+ if data[k] then
+ local lib = require(t)
+ if not lib.istype(data[k]) then
+ return nil, "x509.extension.new: expect data." .. k .. " to be a " .. t .. " instance"
+ end
+ args[k] = data[k].ctx
+ end
+ end
+ C.X509V3_set_ctx(x509_ctx_ptr[0], args.issuer, args.subject, args.request, args.crl, 0)
+
+ if OPENSSL_3X and args.issuer_pkey then
+ if C.X509V3_set_issuer_pkey(x509_ctx_ptr[0], args.issuer_pkey) ~= 1 then
+ return nil, format_error("x509.extension.new: X509V3_set_issuer_pkey")
+ end
+ end
+
+ elseif type(data) == 'string' then
+ err = nconf_load(conf, data)
+ if err then
+ return nil, "x509.extension.new: " .. err
+ end
+ elseif data then
+ return nil, "x509.extension.new: expect nil, string a table at #3"
+ end
+
+ -- setting conf is required for some extensions to load
+ -- crypto/x509/v3_conf.c:do_ext_conf "else if (method->r2i) {" branch
+ C.X509V3_set_nconf(x509_ctx_ptr[0], conf)
+
+ local ctx = C.X509V3_EXT_nconf_nid(conf, x509_ctx_ptr[0], nid, value)
+ if ctx == nil then
+ return nil, format_error("x509.extension.new")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_extension_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_extension_ptr_ct, ctx) then
+ return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
+ end
+ local ctx = C.X509_EXTENSION_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.extension.dup: X509_EXTENSION_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.from_der(value, txtnid, crit)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.from_der: " .. err
+ end
+ if type(value) ~= 'string' then
+ return nil, "x509.extension.from_der: expect string at #1"
+ end
+
+ local asn1 = C.ASN1_STRING_new()
+ if asn1 == nil then
+ return nil, format_error("x509.extension.from_der: ASN1_STRING_new")
+ end
+ ffi_gc(asn1, C.ASN1_STRING_free)
+
+ if C.ASN1_STRING_set(asn1, value, #value) ~= 1 then
+ return nil, format_error("x509.extension.from_der: ASN1_STRING_set")
+ end
+
+ local ctx = C.X509_EXTENSION_create_by_NID(nil, nid, crit and 1 or 0, asn1)
+ if ctx == nil then
+ return nil, format_error("x509.extension.from_der: X509_EXTENSION_create_by_NID")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:to_der()
+ local asn1 = C.X509_EXTENSION_get_data(self.ctx)
+
+ return ffi_str(asn1_macro.ASN1_STRING_get0_data(asn1))
+end
+
+function _M.from_data(any, txtnid, crit)
+ local nid, err = objects_lib.txtnid2nid(txtnid)
+ if err then
+ return nil, "x509.extension.from_der: " .. err
+ end
+
+ if type(any) ~= "table" or type(any.ctx) ~= "cdata" then
+ return nil, "x509.extension.from_data: expect a table with ctx at #1"
+ elseif type(nid) ~= "number" then
+ return nil, "x509.extension.from_data: expect a table at #2"
+ end
+
+ local ctx = C.X509V3_EXT_i2d(nid, crit and 1 or 0, any.ctx)
+ if ctx == nil then
+ return nil, format_error("x509.extension.from_data: X509V3_EXT_i2d")
+ end
+ ffi_gc(ctx, C.X509_EXTENSION_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+function _M.to_data(extension, nid)
+ if not _M.istype(extension) then
+ return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
+ elseif type(nid) ~= "number" then
+ return nil, "x509.extension.to_data: expect a table at #2"
+ end
+
+ local void_ptr = C.X509V3_EXT_d2i(extension.ctx)
+ if void_ptr == nil then
+ return nil, format_error("x509.extension:to_data: X509V3_EXT_d2i")
+ end
+
+ if nid == NID_subject_alt_name then
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ ffi_gc(void_ptr, stack_lib.gc_of("GENERAL_NAME"))
+ local got = ffi_cast("GENERAL_NAMES*", void_ptr)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+ end
+
+ return nil, string.format("x509.extension:to_data: don't know how to convert to NID %d", nid)
+end
+
+function _M:get_object()
+ -- retruns the internal pointer
+ local asn1 = C.X509_EXTENSION_get_object(self.ctx)
+
+ return objects_lib.obj2table(asn1)
+end
+
+function _M:get_critical()
+ return C.X509_EXTENSION_get_critical(self.ctx) == 1
+end
+
+function _M:set_critical(crit)
+ if C.X509_EXTENSION_set_critical(self.ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509.extension:set_critical")
+ end
+ return true
+end
+
+function _M:tostring()
+ local ret, err = bio_util.read_wrap(C.X509V3_EXT_print, self.ctx, 0, 0)
+ if not err then
+ return ret
+ end
+ -- fallback to ASN.1 print
+ local asn1 = C.X509_EXTENSION_get_data(self.ctx)
+ return bio_util.read_wrap(C.ASN1_STRING_print, asn1)
+end
+
+_M.text = _M.tostring
+
+mt.__tostring = function(tbl)
+ local txt, err = _M.text(tbl)
+ if err then
+ error(err)
+ end
+ return txt
+end
+
+
+return _M
diff --git a/server/resty/openssl/x509/extension/dist_points.lua b/server/resty/openssl/x509/extension/dist_points.lua
new file mode 100644
index 0000000..b1d419b
--- /dev/null
+++ b/server/resty/openssl/x509/extension/dist_points.lua
@@ -0,0 +1,75 @@
+local ffi = require "ffi"
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+local altname_lib = require "resty.openssl.x509.altname"
+local stack_lib = require "resty.openssl.stack"
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "DIST_POINT"
+local new = stack_lib.new_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+-- TODO: return other attributes?
+local cdp_decode_fullname = function(ctx)
+ return altname_lib.dup(ctx.distpoint.name.fullname)
+end
+
+local mt = stack_lib.mt_of(STACK, cdp_decode_fullname, _M)
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "OPENSSL_sk_new_null() failed"
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(stack_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "expect a stack ctx at #1"
+ end
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+_M.all = function(stack)
+ local ret = {}
+ local _next = mt.__ipairs(stack)
+ while true do
+ local i, e = _next()
+ if i then
+ ret[i] = e
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/extension/info_access.lua b/server/resty/openssl/x509/extension/info_access.lua
new file mode 100644
index 0000000..21025a8
--- /dev/null
+++ b/server/resty/openssl/x509/extension/info_access.lua
@@ -0,0 +1,137 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.err"
+local altname_lib = require "resty.openssl.x509.altname"
+local stack_lib = require "resty.openssl.stack"
+
+local _M = {}
+
+local authority_info_access_ptr_ct = ffi.typeof("AUTHORITY_INFO_ACCESS*")
+
+local STACK = "ACCESS_DESCRIPTION"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+
+local aia_decode = function(ctx)
+ local nid = C.OBJ_obj2nid(ctx.method)
+ local gn = altname_lib.gn_decode(ctx.location)
+ return { nid, unpack(gn) }
+end
+
+local mt = stack_lib.mt_of(STACK, aia_decode, _M)
+local mt__pairs = mt.__pairs
+mt.__pairs = function(tbl)
+ local f = mt__pairs(tbl)
+ return function()
+ local _, e = f()
+ if not e then return end
+ return unpack(e)
+ end
+end
+
+function _M.new()
+ local ctx = new()
+ if ctx == nil then
+ return nil, "OPENSSL_sk_new_null() failed"
+ end
+ local cast = ffi_cast("AUTHORITY_INFO_ACCESS*", ctx)
+
+ local self = setmetatable({
+ ctx = ctx,
+ cast = cast,
+ _is_shallow_copy = false,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.cast and ffi.istype(authority_info_access_ptr_ct, l.cast)
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(authority_info_access_ptr_ct, ctx) then
+ return nil, "expect a AUTHORITY_INFO_ACCESS* ctx at #1"
+ end
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ cast = ffi_cast("AUTHORITY_INFO_ACCESS*", dup_ctx),
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+function _M:add(nid, typ, value)
+ -- the stack element stays with stack
+ -- we shouldn't add gc handler if it's already been
+ -- pushed to stack. instead, rely on the gc handler
+ -- of the stack to release all memories
+ local ad = C.ACCESS_DESCRIPTION_new()
+ if ad == nil then
+ return nil, "ACCESS_DESCRIPTION_new() failed"
+ end
+
+ -- C.ASN1_OBJECT_free(ad.method)
+
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ C.ACCESS_DESCRIPTION_free(ad)
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, "invalid NID text " .. (nid or "nil")
+ end
+
+ ad.method = asn1
+
+ local err = altname_lib.gn_set(ad.location, typ, value)
+ if err then
+ C.ACCESS_DESCRIPTION_free(ad)
+ return nil, err
+ end
+
+ local _, err = add(self.ctx, ad)
+ if err then
+ C.ACCESS_DESCRIPTION_free(ad)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(ad, C.ACCESS_DESCRIPTION_free)
+ self._elem_refs[self._elem_refs_idx] = ad
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+ return self
+end
+
+_M.all = function(stack)
+ local ret = {}
+ local _next = mt.__ipairs(stack)
+ while true do
+ local i, e = _next()
+ if i then
+ ret[i] = e
+ else
+ break
+ end
+ end
+ return ret
+end
+
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/extensions.lua b/server/resty/openssl/x509/extensions.lua
new file mode 100644
index 0000000..3b64b8a
--- /dev/null
+++ b/server/resty/openssl/x509/extensions.lua
@@ -0,0 +1,84 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+local stack_lib = require "resty.openssl.stack"
+local extension_lib = require "resty.openssl.x509.extension"
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+
+local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
+
+local STACK = "X509_EXTENSION"
+local new = stack_lib.new_of(STACK)
+local add = stack_lib.add_of(STACK)
+local dup = stack_lib.dup_of(STACK)
+local mt = stack_lib.mt_of(STACK, extension_lib.dup, _M)
+
+function _M.new()
+ local raw = new()
+
+ local self = setmetatable({
+ stack_of = STACK,
+ ctx = raw,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
+ and l.stack_of and l.stack_of == STACK
+end
+
+function _M.dup(ctx)
+ if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
+ return nil, "x509.extensions.dup: expect a stack ctx at #1, got " .. type(ctx)
+ end
+
+ local dup_ctx = dup(ctx)
+
+ return setmetatable({
+ ctx = dup_ctx,
+ -- don't let lua gc the original stack to keep its elements
+ _dupped_from = ctx,
+ _is_shallow_copy = true,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt), nil
+end
+
+function _M:add(extension)
+ if not extension_lib.istype(extension) then
+ return nil, "expect a x509.extension instance at #1"
+ end
+
+ local dup = C.X509_EXTENSION_dup(extension.ctx)
+ if dup == nil then
+ return nil, format_error("extensions:add: X509_EXTENSION_dup")
+ end
+
+ local _, err = add(self.ctx, dup)
+ if err then
+ C.X509_EXTENSION_free(dup)
+ return nil, err
+ end
+
+ -- if the stack is duplicated, the gc handler is not pop_free
+ -- handle the gc by ourselves
+ if self._is_shallow_copy then
+ ffi_gc(dup, C.X509_EXTENSION_free)
+ self._elem_refs[self._elem_refs_idx] = dup
+ self._elem_refs_idx = self._elem_refs_idx + 1
+ end
+
+ return true
+end
+
+_M.all = stack_lib.all_func(mt)
+_M.each = mt.__ipairs
+_M.index = mt.__index
+_M.count = mt.__len
+
+return _M
diff --git a/server/resty/openssl/x509/init.lua b/server/resty/openssl/x509/init.lua
new file mode 100644
index 0000000..5c259c8
--- /dev/null
+++ b/server/resty/openssl/x509/init.lua
@@ -0,0 +1,1071 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+local ffi_cast = ffi.cast
+
+require "resty.openssl.include.x509"
+require "resty.openssl.include.x509v3"
+require "resty.openssl.include.evp"
+require "resty.openssl.include.objects"
+local stack_macro = require("resty.openssl.include.stack")
+local stack_lib = require("resty.openssl.stack")
+local asn1_lib = require("resty.openssl.asn1")
+local digest_lib = require("resty.openssl.digest")
+local extension_lib = require("resty.openssl.x509.extension")
+local pkey_lib = require("resty.openssl.pkey")
+local bio_util = require "resty.openssl.auxiliary.bio"
+local txtnid2nid = require("resty.openssl.objects").txtnid2nid
+local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
+local ctypes = require "resty.openssl.auxiliary.ctypes"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_error
+local version = require("resty.openssl.version")
+local OPENSSL_10 = version.OPENSSL_10
+local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
+local OPENSSL_3X = version.OPENSSL_3X
+local BORINGSSL = version.BORINGSSL
+local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
+
+-- accessors provides an openssl version neutral interface to lua layer
+-- it doesn't handle any error, expect that to be implemented in
+-- _M.set_X or _M.get_X
+local accessors = {}
+
+accessors.get_pubkey = C.X509_get_pubkey -- returns new evp_pkey instance, don't need to dup
+accessors.set_pubkey = C.X509_set_pubkey
+accessors.set_version = C.X509_set_version
+accessors.set_serial_number = C.X509_set_serialNumber
+accessors.get_subject_name = C.X509_get_subject_name -- returns internal ptr, we dup it
+accessors.set_subject_name = C.X509_set_subject_name
+accessors.get_issuer_name = C.X509_get_issuer_name -- returns internal ptr, we dup it
+accessors.set_issuer_name = C.X509_set_issuer_name
+accessors.get_signature_nid = C.X509_get_signature_nid
+
+-- generally, use get1 if we return a lua table wrapped ctx which doesn't support dup.
+-- in that case, a new struct is returned from C api, and we will handle gc.
+-- openssl will increment the reference count for returned ptr, and won't free it when
+-- parent struct is freed.
+-- otherwise, use get0, which returns an internal pointer, we don't need to free it up.
+-- it will be gone together with the parent struct.
+
+if BORINGSSL_110 then
+ accessors.get_not_before = C.X509_get0_notBefore -- returns internal ptr, we convert to number
+ accessors.set_not_before = C.X509_set_notBefore
+ accessors.get_not_after = C.X509_get0_notAfter -- returns internal ptr, we convert to number
+ accessors.set_not_after = C.X509_set_notAfter
+ accessors.get_version = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(x509.cert_info.version)
+ end
+ accessors.get_serial_number = C.X509_get_serialNumber -- returns internal ptr, we convert to bn
+elseif OPENSSL_11_OR_LATER then
+ accessors.get_not_before = C.X509_get0_notBefore -- returns internal ptr, we convert to number
+ accessors.set_not_before = C.X509_set1_notBefore
+ accessors.get_not_after = C.X509_get0_notAfter -- returns internal ptr, we convert to number
+ accessors.set_not_after = C.X509_set1_notAfter
+ accessors.get_version = C.X509_get_version -- returns int
+ accessors.get_serial_number = C.X509_get0_serialNumber -- returns internal ptr, we convert to bn
+elseif OPENSSL_10 then
+ accessors.get_not_before = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return x509.cert_info.validity.notBefore
+ end
+ accessors.set_not_before = C.X509_set_notBefore
+ accessors.get_not_after = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return x509.cert_info.validity.notAfter
+ end
+ accessors.set_not_after = C.X509_set_notAfter
+ accessors.get_version = function(x509)
+ if x509 == nil or x509.cert_info == nil or x509.cert_info.validity == nil then
+ return nil
+ end
+ return C.ASN1_INTEGER_get(x509.cert_info.version)
+ end
+ accessors.get_serial_number = C.X509_get_serialNumber -- returns internal ptr, we convert to bn
+end
+
+local function __tostring(self, fmt)
+ if not fmt or fmt == 'PEM' then
+ return bio_util.read_wrap(C.PEM_write_bio_X509, self.ctx)
+ elseif fmt == 'DER' then
+ return bio_util.read_wrap(C.i2d_X509_bio, self.ctx)
+ else
+ return nil, "x509:tostring: can only write PEM or DER format, not " .. fmt
+ end
+end
+
+local _M = {}
+local mt = { __index = _M, __tostring = __tostring }
+
+
+local x509_ptr_ct = ffi.typeof("X509*")
+
+-- only PEM format is supported for now
+function _M.new(cert, fmt, properties)
+ local ctx
+ if not cert then
+ -- routine for create a new cert
+ if OPENSSL_3X then
+ ctx = C.X509_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_new()
+ end
+ if ctx == nil then
+ return nil, format_error("x509.new")
+ end
+ ffi_gc(ctx, C.X509_free)
+
+ C.X509_gmtime_adj(accessors.get_not_before(ctx), 0)
+ C.X509_gmtime_adj(accessors.get_not_after(ctx), 0)
+ elseif type(cert) == "string" then
+ -- routine for load an existing cert
+ local bio = C.BIO_new_mem_buf(cert, #cert)
+ if bio == nil then
+ return nil, format_error("x509.new: BIO_new_mem_buf")
+ end
+
+ fmt = fmt or "*"
+ while true do -- luacheck: ignore 512 -- loop is executed at most once
+ if fmt == "PEM" or fmt == "*" then
+ ctx = C.PEM_read_bio_X509(bio, nil, nil, nil)
+ if ctx ~= nil then
+ break
+ elseif fmt == "*" then
+ -- BIO_reset; #define BIO_CTRL_RESET 1
+ local code = C.BIO_ctrl(bio, 1, 0, nil)
+ if code ~= 1 then
+ C.BIO_free(bio)
+ return nil, "x509.new: BIO_ctrl() failed: " .. code
+ end
+ end
+ end
+ if fmt == "DER" or fmt == "*" then
+ ctx = C.d2i_X509_bio(bio, nil)
+ end
+ break
+ end
+ C.BIO_free(bio)
+ if ctx == nil then
+ return nil, format_error("x509.new")
+ end
+ -- clear errors occur when trying
+ C.ERR_clear_error()
+ ffi_gc(ctx, C.X509_free)
+ elseif type(cert) == 'cdata' then
+ if ffi.istype(x509_ptr_ct, cert) then
+ ctx = cert
+ ffi_gc(ctx, C.X509_free)
+ else
+ return nil, "x509.new: expect a X509* cdata at #1"
+ end
+ else
+ return nil, "x509.new: expect nil or a string at #1"
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_ptr_ct, ctx) then
+ return nil, "x509.dup: expect a x509 ctx at #1"
+ end
+ local ctx = C.X509_dup(ctx)
+ if ctx == nil then
+ return nil, "x509.dup: X509_dup() failed"
+ end
+
+ ffi_gc(ctx, C.X509_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:tostring(fmt)
+ return __tostring(self, fmt)
+end
+
+function _M:to_PEM()
+ return __tostring(self, "PEM")
+end
+
+function _M:set_lifetime(not_before, not_after)
+ local ok, err
+ if not_before then
+ ok, err = self:set_not_before(not_before)
+ if err then
+ return ok, err
+ end
+ end
+
+ if not_after then
+ ok, err = self:set_not_after(not_after)
+ if err then
+ return ok, err
+ end
+ end
+
+ return true
+end
+
+function _M:get_lifetime()
+ local not_before, err = self:get_not_before()
+ if not_before == nil then
+ return nil, nil, err
+ end
+ local not_after, err = self:get_not_after()
+ if not_after == nil then
+ return nil, nil, err
+ end
+
+ return not_before, not_after, nil
+end
+
+-- note: index is 0 based
+local OPENSSL_STRING_value_at = function(ctx, i)
+ local ct = ffi_cast("OPENSSL_STRING", stack_macro.OPENSSL_sk_value(ctx, i))
+ if ct == nil then
+ return nil
+ end
+ return ffi_str(ct)
+end
+
+function _M:get_ocsp_url(return_all)
+ local st = C.X509_get1_ocsp(self.ctx)
+
+ local count = stack_macro.OPENSSL_sk_num(st)
+ if count == 0 then
+ return
+ end
+
+ local ret
+ if return_all then
+ ret = {}
+ for i=0,count-1 do
+ ret[i+1] = OPENSSL_STRING_value_at(st, i)
+ end
+ else
+ ret = OPENSSL_STRING_value_at(st, 0)
+ end
+
+ C.X509_email_free(st)
+ return ret
+end
+
+function _M:get_ocsp_request()
+
+end
+
+function _M:get_crl_url(return_all)
+ local cdp, err = self:get_crl_distribution_points()
+ if err then
+ return nil, err
+ end
+
+ if not cdp or cdp:count() == 0 then
+ return
+ end
+
+ if return_all then
+ local ret = {}
+ local cdp_iter = cdp:each()
+ while true do
+ local _, gn = cdp_iter()
+ if not gn then
+ break
+ end
+ local gn_iter = gn:each()
+ while true do
+ local k, v = gn_iter()
+ if not k then
+ break
+ elseif k == "URI" then
+ table.insert(ret, v)
+ end
+ end
+ end
+ return ret
+ else
+ local gn, err = cdp:index(1)
+ if err then
+ return nil, err
+ end
+ local iter = gn:each()
+ while true do
+ local k, v = iter()
+ if not k then
+ break
+ elseif k == "URI" then
+ return v
+ end
+ end
+ end
+end
+
+local digest_length = ctypes.ptr_of_uint()
+local digest_buf, digest_buf_size
+local function digest(self, cfunc, typ, properties)
+ -- TODO: dedup the following with resty.openssl.digest
+ local ctx
+ if OPENSSL_11_OR_LATER then
+ ctx = C.EVP_MD_CTX_new()
+ ffi_gc(ctx, C.EVP_MD_CTX_free)
+ elseif OPENSSL_10 then
+ ctx = C.EVP_MD_CTX_create()
+ ffi_gc(ctx, C.EVP_MD_CTX_destroy)
+ end
+ if ctx == nil then
+ return nil, "x509:digest: failed to create EVP_MD_CTX"
+ end
+
+ local algo
+ if OPENSSL_3X then
+ algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), typ or 'sha1', properties)
+ else
+ algo = C.EVP_get_digestbyname(typ or 'sha1')
+ end
+ if algo == nil then
+ return nil, string.format("x509:digest: invalid digest type \"%s\"", typ)
+ end
+
+ local md_size = OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)
+ if not digest_buf or digest_buf_size < md_size then
+ digest_buf = ctypes.uchar_array(md_size)
+ digest_buf_size = md_size
+ end
+
+ if cfunc(self.ctx, algo, digest_buf, digest_length) ~= 1 then
+ return nil, format_error("x509:digest")
+ end
+
+ return ffi_str(digest_buf, digest_length[0])
+end
+
+function _M:digest(typ, properties)
+ return digest(self, C.X509_digest, typ, properties)
+end
+
+function _M:pubkey_digest(typ, properties)
+ return digest(self, C.X509_pubkey_digest, typ, properties)
+end
+
+function _M:check_private_key(key)
+ if not pkey_lib.istype(key) then
+ return false, "x509:check_private_key: except a pkey instance at #1"
+ end
+
+ if not key:is_private() then
+ return false, "x509:check_private_key: not a private key"
+ end
+
+ if C.X509_check_private_key(self.ctx, key.ctx) == 1 then
+ return true
+ end
+ return false, format_error("x509:check_private_key")
+end
+
+-- START AUTO GENERATED CODE
+
+-- AUTO GENERATED
+function _M:sign(pkey, digest)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509:sign: expect a pkey instance at #1"
+ end
+
+ local digest_algo
+ if digest then
+ if not digest_lib.istype(digest) then
+ return false, "x509:sign: expect a digest instance at #2"
+ elseif not digest.algo then
+ return false, "x509:sign: expect a digest instance to have algo member"
+ end
+ digest_algo = digest.algo
+ elseif BORINGSSL then
+ digest_algo = C.EVP_get_digestbyname('sha256')
+ end
+
+ -- returns size of signature if success
+ if C.X509_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
+ return false, format_error("x509:sign")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:verify(pkey)
+ if not pkey_lib.istype(pkey) then
+ return false, "x509:verify: expect a pkey instance at #1"
+ end
+
+ local code = C.X509_verify(self.ctx, pkey.ctx)
+ if code == 1 then
+ return true
+ elseif code == 0 then
+ return false
+ else -- typically -1
+ return false, format_error("x509:verify", code)
+ end
+end
+
+-- AUTO GENERATED
+local function get_extension(ctx, nid_txt, last_pos)
+ last_pos = (last_pos or 0) - 1
+ local nid, err = txtnid2nid(nid_txt)
+ if err then
+ return nil, nil, err
+ end
+ local pos = C.X509_get_ext_by_NID(ctx, nid, last_pos)
+ if pos == -1 then
+ return nil
+ end
+ local ctx = C.X509_get_ext(ctx, pos)
+ if ctx == nil then
+ return nil, nil, format_error()
+ end
+ return ctx, pos
+end
+
+-- AUTO GENERATED
+function _M:add_extension(extension)
+ if not extension_lib.istype(extension) then
+ return false, "x509:add_extension: expect a x509.extension instance at #1"
+ end
+
+ -- X509_add_ext returnes the stack on success, and NULL on error
+ -- the X509_EXTENSION ctx is dupped internally
+ if C.X509_add_ext(self.ctx, extension.ctx, -1) == nil then
+ return false, format_error("x509:add_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension(nid_txt, last_pos)
+ local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, nil, "x509:get_extension: " .. err
+ end
+ local ext, err = extension_lib.dup(ctx)
+ if err then
+ return nil, nil, "x509:get_extension: " .. err
+ end
+ return ext, pos+1
+end
+
+local X509_delete_ext
+if OPENSSL_11_OR_LATER then
+ X509_delete_ext = C.X509_delete_ext
+elseif OPENSSL_10 then
+ X509_delete_ext = function(ctx, pos)
+ return C.X509v3_delete_ext(ctx.cert_info.extensions, pos)
+ end
+else
+ X509_delete_ext = function(...)
+ error("X509_delete_ext undefined")
+ end
+end
+
+-- AUTO GENERATED
+function _M:set_extension(extension, last_pos)
+ if not extension_lib.istype(extension) then
+ return false, "x509:set_extension: expect a x509.extension instance at #1"
+ end
+
+ last_pos = (last_pos or 0) - 1
+
+ local nid = extension:get_object().nid
+ local pos = C.X509_get_ext_by_NID(self.ctx, nid, last_pos)
+ -- pos may be -1, which means not found, it's fine, we will add new one instead of replace
+
+ local removed = X509_delete_ext(self.ctx, pos)
+ C.X509_EXTENSION_free(removed)
+
+ if C.X509_add_ext(self.ctx, extension.ctx, pos) == nil then
+ return false, format_error("x509:set_extension")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:set_extension_critical(nid_txt, crit, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509:set_extension_critical: " .. err
+ end
+
+ if C.X509_EXTENSION_set_critical(ctx, crit and 1 or 0) ~= 1 then
+ return false, format_error("x509:set_extension_critical")
+ end
+
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_extension_critical(nid_txt, last_pos)
+ local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
+ if err then
+ return nil, "x509:get_extension_critical: " .. err
+ end
+
+ return C.X509_EXTENSION_get_critical(ctx) == 1
+end
+
+-- AUTO GENERATED
+function _M:get_serial_number()
+ local got = accessors.get_serial_number(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ -- returns a new BIGNUM instance
+ got = C.ASN1_INTEGER_to_BN(got, nil)
+ if got == nil then
+ return false, format_error("x509:set: BN_to_ASN1_INTEGER")
+ end
+ -- bn will be duplicated thus this ctx should be freed up
+ ffi_gc(got, C.BN_free)
+
+ local lib = require("resty.openssl.bn")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_serial_number(toset)
+ local lib = require("resty.openssl.bn")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_serial_number: expect a bn instance at #1"
+ end
+ toset = toset.ctx
+
+ toset = C.BN_to_ASN1_INTEGER(toset, nil)
+ if toset == nil then
+ return false, format_error("x509:set: BN_to_ASN1_INTEGER")
+ end
+ -- "A copy of the serial number is used internally
+ -- so serial should be freed up after use.""
+ ffi_gc(toset, C.ASN1_INTEGER_free)
+
+ if accessors.set_serial_number(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_serial_number")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_not_before()
+ local got = accessors.get_not_before(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_not_before(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_not_before: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_not_before(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_not_before")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_not_after()
+ local got = accessors.get_not_after(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = asn1_lib.asn1_to_unix(got)
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_not_after(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_not_after: expect a number at #1"
+ end
+
+ toset = C.ASN1_TIME_set(nil, toset)
+ ffi_gc(toset, C.ASN1_STRING_free)
+
+ if accessors.set_not_after(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_not_after")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_pubkey()
+ local got = accessors.get_pubkey(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.pkey")
+ -- returned a copied instance directly
+ return lib.new(got)
+end
+
+-- AUTO GENERATED
+function _M:set_pubkey(toset)
+ local lib = require("resty.openssl.pkey")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_pubkey: expect a pkey instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_pubkey(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_pubkey")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_subject_name()
+ local got = accessors.get_subject_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_subject_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_subject_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_subject_name(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_subject_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_issuer_name()
+ local got = accessors.get_issuer_name(self.ctx)
+ if got == nil then
+ return nil
+ end
+ local lib = require("resty.openssl.x509.name")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED
+function _M:set_issuer_name(toset)
+ local lib = require("resty.openssl.x509.name")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_issuer_name: expect a x509.name instance at #1"
+ end
+ toset = toset.ctx
+ if accessors.set_issuer_name(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_issuer_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED
+function _M:get_version()
+ local got = accessors.get_version(self.ctx)
+ if got == nil then
+ return nil
+ end
+
+ got = tonumber(got) + 1
+
+ return got
+end
+
+-- AUTO GENERATED
+function _M:set_version(toset)
+ if type(toset) ~= "number" then
+ return false, "x509:set_version: expect a number at #1"
+ end
+
+ -- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
+ -- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
+ toset = toset - 1
+
+ if accessors.set_version(self.ctx, toset) == 0 then
+ return false, format_error("x509:set_version")
+ end
+ return true
+end
+
+local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
+assert(NID_subject_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_subject_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_subject_alt_name: extension of subject_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_subject_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_subject_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_subject_alt_name, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_subject_alt_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_subject_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_subject_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_subject_alt_name_critical()
+ return _M.get_extension_critical(self, NID_subject_alt_name)
+end
+
+local NID_issuer_alt_name = C.OBJ_sn2nid("issuerAltName")
+assert(NID_issuer_alt_name ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_issuer_alt_name()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_issuer_alt_name, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_issuer_alt_name: extension of issuer_alt_name occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_issuer_alt_name")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a GENERAL_NAME
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
+ got = ffi_cast("GENERAL_NAMES*", got_ref)
+ local lib = require("resty.openssl.x509.altname")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_issuer_alt_name(toset)
+ local lib = require("resty.openssl.x509.altname")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_issuer_alt_name: expect a x509.altname instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_issuer_alt_name, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_issuer_alt_name")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_issuer_alt_name_critical(crit)
+ return _M.set_extension_critical(self, NID_issuer_alt_name, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_issuer_alt_name_critical()
+ return _M.get_extension_critical(self, NID_issuer_alt_name)
+end
+
+local NID_basic_constraints = C.OBJ_sn2nid("basicConstraints")
+assert(NID_basic_constraints ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_basic_constraints(name)
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_basic_constraints, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_basic_constraints: extension of basic_constraints occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_basic_constraints")
+ end
+
+ local ctx = ffi_cast("BASIC_CONSTRAINTS*", got)
+
+ local ca = ctx.ca == 0xFF
+ local pathlen = tonumber(C.ASN1_INTEGER_get(ctx.pathlen))
+
+ C.BASIC_CONSTRAINTS_free(ctx)
+
+ if not name or type(name) ~= "string" then
+ got = {
+ ca = ca,
+ pathlen = pathlen,
+ }
+ elseif string.lower(name) == "ca" then
+ got = ca
+ elseif string.lower(name) == "pathlen" then
+ got = pathlen
+ end
+
+ return got
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_basic_constraints(toset)
+ if type(toset) ~= "table" then
+ return false, "x509:set_basic_constraints: expect a table at #1"
+ end
+
+ local cfg_lower = {}
+ for k, v in pairs(toset) do
+ cfg_lower[string.lower(k)] = v
+ end
+
+ toset = C.BASIC_CONSTRAINTS_new()
+ if toset == nil then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS")
+ end
+ ffi_gc(toset, C.BASIC_CONSTRAINTS_free)
+
+ toset.ca = cfg_lower.ca and 0xFF or 0
+ local pathlen = cfg_lower.pathlen and tonumber(cfg_lower.pathlen)
+ if pathlen then
+ C.ASN1_INTEGER_free(toset.pathlen)
+
+ local asn1 = C.ASN1_STRING_type_new(pathlen)
+ if asn1 == nil then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS: ASN1_STRING_type_new")
+ end
+ toset.pathlen = asn1
+
+ local code = C.ASN1_INTEGER_set(asn1, pathlen)
+ if code ~= 1 then
+ return false, format_error("x509:set_BASIC_CONSTRAINTS: ASN1_INTEGER_set", code)
+ end
+ end
+
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_basic_constraints, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_basic_constraints")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_basic_constraints_critical(crit)
+ return _M.set_extension_critical(self, NID_basic_constraints, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_basic_constraints_critical()
+ return _M.get_extension_critical(self, NID_basic_constraints)
+end
+
+local NID_info_access = C.OBJ_sn2nid("authorityInfoAccess")
+assert(NID_info_access ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_info_access()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_info_access, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_info_access: extension of info_access occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_info_access")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a ACCESS_DESCRIPTION
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("ACCESS_DESCRIPTION"))
+ got = ffi_cast("AUTHORITY_INFO_ACCESS*", got_ref)
+ local lib = require("resty.openssl.x509.extension.info_access")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_info_access(toset)
+ local lib = require("resty.openssl.x509.extension.info_access")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_info_access: expect a x509.extension.info_access instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_info_access, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_info_access")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_info_access_critical(crit)
+ return _M.set_extension_critical(self, NID_info_access, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_info_access_critical()
+ return _M.get_extension_critical(self, NID_info_access)
+end
+
+local NID_crl_distribution_points = C.OBJ_sn2nid("crlDistributionPoints")
+assert(NID_crl_distribution_points ~= 0)
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_crl_distribution_points()
+ local crit = ctypes.ptr_of_int()
+ -- X509_get_ext_d2i returns internal pointer, always dup
+ -- for now this function always returns the first found extension
+ local got = C.X509_get_ext_d2i(self.ctx, NID_crl_distribution_points, crit, nil)
+ crit = tonumber(crit[0])
+ if crit == -1 then -- not found
+ return nil
+ elseif crit == -2 then
+ return nil, "x509:get_crl_distribution_points: extension of crl_distribution_points occurs more than one times, " ..
+ "this is not yet implemented. Please use get_extension instead."
+ elseif got == nil then
+ return nil, format_error("x509:get_crl_distribution_points")
+ end
+
+ -- Note: here we only free the stack itself not elements
+ -- since there seems no way to increase ref count for a DIST_POINT
+ -- we left the elements referenced by the new-dup'ed stack
+ local got_ref = got
+ ffi_gc(got_ref, stack_lib.gc_of("DIST_POINT"))
+ got = ffi_cast("OPENSSL_STACK*", got_ref)
+ local lib = require("resty.openssl.x509.extension.dist_points")
+ -- the internal ptr is returned, ie we need to copy it
+ return lib.dup(got)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_crl_distribution_points(toset)
+ local lib = require("resty.openssl.x509.extension.dist_points")
+ if lib.istype and not lib.istype(toset) then
+ return false, "x509:set_crl_distribution_points: expect a x509.extension.dist_points instance at #1"
+ end
+ toset = toset.ctx
+ -- x509v3.h: # define X509V3_ADD_REPLACE 2L
+ if C.X509_add1_ext_i2d(self.ctx, NID_crl_distribution_points, toset, 0, 0x2) ~= 1 then
+ return false, format_error("x509:set_crl_distribution_points")
+ end
+ return true
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:set_crl_distribution_points_critical(crit)
+ return _M.set_extension_critical(self, NID_crl_distribution_points, crit)
+end
+
+-- AUTO GENERATED: EXTENSIONS
+function _M:get_crl_distribution_points_critical()
+ return _M.get_extension_critical(self, NID_crl_distribution_points)
+end
+
+
+-- AUTO GENERATED
+function _M:get_signature_nid()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_nid")
+ end
+
+ return nid
+end
+
+-- AUTO GENERATED
+function _M:get_signature_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_name")
+ end
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+
+-- AUTO GENERATED
+function _M:get_signature_digest_name()
+ local nid = accessors.get_signature_nid(self.ctx)
+ if nid <= 0 then
+ return nil, format_error("x509:get_signature_digest_name")
+ end
+
+ local nid = find_sigid_algs(nid)
+
+ return ffi.string(C.OBJ_nid2sn(nid))
+end
+-- END AUTO GENERATED CODE
+
+return _M
diff --git a/server/resty/openssl/x509/name.lua b/server/resty/openssl/x509/name.lua
new file mode 100644
index 0000000..f83fcc1
--- /dev/null
+++ b/server/resty/openssl/x509/name.lua
@@ -0,0 +1,156 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+
+require "resty.openssl.include.x509.name"
+require "resty.openssl.include.err"
+local objects_lib = require "resty.openssl.objects"
+local asn1_macro = require "resty.openssl.include.asn1"
+
+-- local MBSTRING_FLAG = 0x1000
+local MBSTRING_ASC = 0x1001 -- (MBSTRING_FLAG|1)
+
+local _M = {}
+
+local x509_name_ptr_ct = ffi.typeof("X509_NAME*")
+
+-- starts from 0
+local function value_at(ctx, i)
+ local entry = C.X509_NAME_get_entry(ctx, i)
+ local obj = C.X509_NAME_ENTRY_get_object(entry)
+ local ret = objects_lib.obj2table(obj)
+
+ local str = C.X509_NAME_ENTRY_get_data(entry)
+ if str ~= nil then
+ ret.blob = ffi_str(asn1_macro.ASN1_STRING_get0_data(str))
+ end
+
+ return ret
+end
+
+local function iter(tbl)
+ local i = 0
+ local n = tonumber(C.X509_NAME_entry_count(tbl.ctx))
+ return function()
+ i = i + 1
+ if i <= n then
+ local obj = value_at(tbl.ctx, i-1)
+ return obj.sn or obj.ln or obj.id, obj
+ end
+ end
+end
+
+local mt = {
+ __index = _M,
+ __pairs = iter,
+ __len = function(tbl) return tonumber(C.X509_NAME_entry_count(tbl.ctx)) end,
+}
+
+function _M.new()
+ local ctx = C.X509_NAME_new()
+ if ctx == nil then
+ return nil, "x509.name.new: X509_NAME_new() failed"
+ end
+ ffi_gc(ctx, C.X509_NAME_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_name_ptr_ct, l.ctx)
+end
+
+function _M.dup(ctx)
+ if not ffi.istype(x509_name_ptr_ct, ctx) then
+ return nil, "x509.name.dup: expect a x509.name ctx at #1, got " .. type(ctx)
+ end
+ local ctx = C.X509_NAME_dup(ctx)
+ ffi_gc(ctx, C.X509_NAME_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+function _M:add(nid, txt)
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, "x509.name:add: invalid NID text " .. (nid or "nil")
+ end
+
+ local code = C.X509_NAME_add_entry_by_OBJ(self.ctx, asn1, MBSTRING_ASC, txt, #txt, -1, 0)
+ C.ASN1_OBJECT_free(asn1)
+
+ if code ~= 1 then
+ return nil, "x509.name:add: X509_NAME_add_entry_by_OBJ() failed"
+ end
+
+ return self
+end
+
+function _M:find(nid, last_pos)
+ local asn1 = C.OBJ_txt2obj(nid, 0)
+ if asn1 == nil then
+ -- clean up error occurs during OBJ_txt2*
+ C.ERR_clear_error()
+ return nil, nil, "x509.name:find: invalid NID text " .. (nid or "nil")
+ end
+ -- make 1-index array to 0-index
+ last_pos = (last_pos or 0) - 1
+
+ local pos = C.X509_NAME_get_index_by_OBJ(self.ctx, asn1, last_pos)
+ if pos == -1 then
+ return nil
+ end
+
+ C.ASN1_OBJECT_free(asn1)
+
+ return value_at(self.ctx, pos), pos+1
+end
+
+-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
+function _M:all()
+ local ret = {}
+ local _next = iter(self)
+ while true do
+ local k, obj = _next()
+ if obj then
+ ret[k] = obj
+ else
+ break
+ end
+ end
+ return ret
+end
+
+function _M:each()
+ return iter(self)
+end
+
+mt.__tostring = function(self)
+ local values = {}
+ local _next = iter(self)
+ while true do
+ local k, v = _next()
+ if k then
+ table.insert(values, k .. "=" .. v.blob)
+ else
+ break
+ end
+ end
+ table.sort(values)
+ return table.concat(values, "/")
+end
+
+_M.tostring = mt.__tostring
+
+return _M
diff --git a/server/resty/openssl/x509/revoked.lua b/server/resty/openssl/x509/revoked.lua
new file mode 100644
index 0000000..9762200
--- /dev/null
+++ b/server/resty/openssl/x509/revoked.lua
@@ -0,0 +1,108 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+
+require "resty.openssl.include.x509.crl"
+require "resty.openssl.include.x509.revoked"
+local bn_lib = require("resty.openssl.bn")
+local format_error = require("resty.openssl.err").format_error
+
+local _M = {}
+local mt = { __index = _M }
+
+local x509_revoked_ptr_ct = ffi.typeof('X509_REVOKED*')
+
+local NID_crl_reason = C.OBJ_txt2nid("CRLReason")
+assert(NID_crl_reason > 0)
+
+--- Creates new instance of X509_REVOKED data
+-- @tparam bn|number sn Serial number as number or bn instance
+-- @tparam number time Revocation time
+-- @tparam number reason Revocation reason
+-- @treturn table instance of the module or nil
+-- @treturn[opt] string Returns optional error message in case of error
+function _M.new(sn, time, reason)
+ --- only convert to bn if it is number
+ if type(sn) == "number"then
+ sn = bn_lib.new(sn)
+ end
+ if not bn_lib.istype(sn) then
+ return nil, "x509.revoked.new: sn should be number or a bn instance"
+ end
+
+ if type(time) ~= "number" then
+ return nil, "x509.revoked.new: expect a number at #2"
+ end
+ if type(reason) ~= "number" then
+ return nil, "x509.revoked.new: expect a number at #3"
+ end
+
+ local ctx = C.X509_REVOKED_new()
+ ffi_gc(ctx, C.X509_REVOKED_free)
+
+ -- serial number
+ local sn_asn1 = C.BN_to_ASN1_INTEGER(sn.ctx, nil)
+ if sn_asn1 == nil then
+ return nil, "x509.revoked.new: BN_to_ASN1_INTEGER() failed"
+ end
+ ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
+
+ if C.X509_REVOKED_set_serialNumber(ctx, sn_asn1) == 0 then
+ return nil, format_error("x509.revoked.new: X509_REVOKED_set_serialNumber()")
+ end
+
+ -- time
+ time = C.ASN1_TIME_set(nil, time)
+ if time == nil then
+ return nil, format_error("x509.revoked.new: ASN1_TIME_set()")
+ end
+ ffi_gc(time, C.ASN1_STRING_free)
+
+ if C.X509_REVOKED_set_revocationDate(ctx, time) == 0 then
+ return nil, format_error("x509.revoked.new: X509_REVOKED_set_revocationDate()")
+ end
+
+ -- reason
+ local reason_asn1 = C.ASN1_ENUMERATED_new()
+ if reason_asn1 == nil then
+ return nil, "x509.revoked.new: ASN1_ENUMERATED_new() failed"
+ end
+ ffi_gc(reason_asn1, C.ASN1_ENUMERATED_free)
+
+ local reason_ext = C.X509_EXTENSION_new()
+ if reason_ext == nil then
+ return nil, "x509.revoked.new: X509_EXTENSION_new() failed"
+ end
+ ffi_gc(reason_ext, C.X509_EXTENSION_free)
+
+ if C.ASN1_ENUMERATED_set(reason_asn1, reason) == 0 then
+ return nil, format_error("x509.revoked.new: ASN1_ENUMERATED_set()")
+ end
+
+ if C.X509_EXTENSION_set_data(reason_ext, reason_asn1) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_data()")
+ end
+
+ if C.X509_EXTENSION_set_object(reason_ext, C.OBJ_nid2obj(NID_crl_reason)) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
+ end
+
+ if C.X509_REVOKED_add_ext(ctx, reason_ext, 0) == 0 then
+ return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
+ end
+
+ local self = setmetatable({
+ ctx = ctx,
+ }, mt)
+
+ return self, nil
+end
+
+--- Type check
+-- @tparam table Instance of revoked module
+-- @treturn boolean true if instance is instance of revoked module false otherwise
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_revoked_ptr_ct, l.ctx)
+end
+
+return _M
diff --git a/server/resty/openssl/x509/store.lua b/server/resty/openssl/x509/store.lua
new file mode 100644
index 0000000..1722e4c
--- /dev/null
+++ b/server/resty/openssl/x509/store.lua
@@ -0,0 +1,227 @@
+local ffi = require "ffi"
+local C = ffi.C
+local ffi_gc = ffi.gc
+local ffi_str = ffi.string
+local bor = bit.bor
+
+local x509_vfy_macro = require "resty.openssl.include.x509_vfy"
+local x509_lib = require "resty.openssl.x509"
+local chain_lib = require "resty.openssl.x509.chain"
+local crl_lib = require "resty.openssl.x509.crl"
+local ctx_lib = require "resty.openssl.ctx"
+local format_error = require("resty.openssl.err").format_all_error
+local format_all_error = require("resty.openssl.err").format_error
+local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
+
+local _M = {}
+local mt = { __index = _M }
+
+_M.verify_flags = x509_vfy_macro.verify_flags
+
+local x509_store_ptr_ct = ffi.typeof('X509_STORE*')
+
+function _M.new()
+ local ctx = C.X509_STORE_new()
+ if ctx == nil then
+ return nil, "x509.store.new: X509_STORE_new() failed"
+ end
+ ffi_gc(ctx, C.X509_STORE_free)
+
+ local self = setmetatable({
+ ctx = ctx,
+ _elem_refs = {},
+ _elem_refs_idx = 1,
+ }, mt)
+
+ return self, nil
+end
+
+function _M.istype(l)
+ return l and l.ctx and ffi.istype(x509_store_ptr_ct, l.ctx)
+end
+
+function _M:use_default(properties)
+ if x509_vfy_macro.X509_STORE_set_default_paths(self.ctx, ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:use_default")
+ end
+ return true
+end
+
+function _M:add(item)
+ local dup
+ local err
+ if x509_lib.istype(item) then
+ dup = C.X509_dup(item.ctx)
+ if dup == nil then
+ return false, "x509.store:add: X509_dup() failed"
+ end
+ -- ref counter of dup is increased by 1
+ if C.X509_STORE_add_cert(self.ctx, dup) ~= 1 then
+ err = format_all_error("x509.store:add: X509_STORE_add_cert")
+ end
+ -- decrease the dup ctx ref count immediately to make leak test happy
+ C.X509_free(dup)
+ elseif crl_lib.istype(item) then
+ dup = C.X509_CRL_dup(item.ctx)
+ if dup == nil then
+ return false, "x509.store:add: X509_CRL_dup() failed"
+ end
+ -- ref counter of dup is increased by 1
+ if C.X509_STORE_add_crl(self.ctx, dup) ~= 1 then
+ err = format_all_error("x509.store:add: X509_STORE_add_crl")
+ end
+
+ -- define X509_V_FLAG_CRL_CHECK 0x4
+ -- enables CRL checking for the certificate chain leaf certificate.
+ -- An error occurs if a suitable CRL cannot be found.
+ -- Note: this does not check for certificates in the chain.
+ if C.X509_STORE_set_flags(self.ctx, 0x4) ~= 1 then
+ return false, format_error("x509.store:add: X509_STORE_set_flags")
+ end
+ -- decrease the dup ctx ref count immediately to make leak test happy
+ C.X509_CRL_free(dup)
+ else
+ return false, "x509.store:add: expect an x509 or crl instance at #1"
+ end
+
+ if err then
+ return false, err
+ end
+
+ -- X509_STORE doesn't have stack gc handler, we need to gc by ourselves
+ self._elem_refs[self._elem_refs_idx] = dup
+ self._elem_refs_idx = self._elem_refs_idx + 1
+
+ return true
+end
+
+function _M:load_file(path, properties)
+ if type(path) ~= "string" then
+ return false, "x509.store:load_file: expect a string at #1"
+ else
+ if x509_vfy_macro.X509_STORE_load_locations(self.ctx, path, nil,
+ ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:load_file")
+ end
+ end
+
+ return true
+end
+
+function _M:load_directory(path, properties)
+ if type(path) ~= "string" then
+ return false, "x509.store:load_directory expect a string at #1"
+ else
+ if x509_vfy_macro.X509_STORE_load_locations(self.ctx, nil, path,
+ ctx_lib.get_libctx(), properties) ~= 1 then
+ return false, format_all_error("x509.store:load_directory")
+ end
+ end
+
+ return true
+end
+
+function _M:set_depth(depth)
+ depth = depth and tonumber(depth)
+ if not depth then
+ return nil, "x509.store:set_depth: expect a number at #1"
+ end
+
+ if C.X509_STORE_set_depth(self.ctx, depth) ~= 1 then
+ return false, format_error("x509.store:set_depth")
+ end
+
+ return true
+end
+
+function _M:set_purpose(purpose)
+ if type(purpose) ~= "string" then
+ return nil, "x509.store:set_purpose: expect a string at #1"
+ end
+
+ local pchar = ffi.new("char[?]", #purpose, purpose)
+ local idx = C.X509_PURPOSE_get_by_sname(pchar)
+ idx = tonumber(idx)
+
+ if idx == -1 then
+ return false, "invalid purpose \"" .. purpose .. "\""
+ end
+
+ local purp = C.X509_PURPOSE_get0(idx)
+ local i = C.X509_PURPOSE_get_id(purp)
+
+ if C.X509_STORE_set_purpose(self.ctx, i) ~= 1 then
+ return false, format_error("x509.store:set_purpose: X509_STORE_set_purpose")
+ end
+
+ return true
+end
+
+function _M:set_flags(...)
+ local flag = 0
+ for _, f in ipairs({...}) do
+ flag = bor(flag, f)
+ end
+
+ if C.X509_STORE_set_flags(self.ctx, flag) ~= 1 then
+ return false, format_error("x509.store:set_flags: X509_STORE_set_flags")
+ end
+
+ return true
+end
+
+function _M:verify(x509, chain, return_chain, properties, verify_method)
+ if not x509_lib.istype(x509) then
+ return nil, "x509.store:verify: expect a x509 instance at #1"
+ elseif chain and not chain_lib.istype(chain) then
+ return nil, "x509.store:verify: expect a x509.chain instance at #1"
+ end
+
+ local ctx
+ if OPENSSL_3X then
+ ctx = C.X509_STORE_CTX_new_ex(ctx_lib.get_libctx(), properties)
+ else
+ ctx = C.X509_STORE_CTX_new()
+ end
+ if ctx == nil then
+ return nil, "x509.store:verify: X509_STORE_CTX_new() failed"
+ end
+
+ ffi_gc(ctx, C.X509_STORE_CTX_free)
+
+ local chain_dup_ctx
+ if chain then
+ local chain_dup, err = chain_lib.dup(chain.ctx)
+ if err then
+ return nil, err
+ end
+ chain_dup_ctx = chain_dup.ctx
+ end
+
+ if C.X509_STORE_CTX_init(ctx, self.ctx, x509.ctx, chain_dup_ctx) ~= 1 then
+ return nil, format_error("x509.store:verify: X509_STORE_CTX_init")
+ end
+
+ if verify_method and C.X509_STORE_CTX_set_default(ctx, verify_method) ~= 1 then
+ return nil, "x509.store:verify: invalid verify_method \"" .. verify_method .. "\""
+ end
+
+ local code = C.X509_verify_cert(ctx)
+ if code == 1 then -- verified
+ if not return_chain then
+ return true, nil
+ end
+ local ret_chain_ctx = x509_vfy_macro.X509_STORE_CTX_get0_chain(ctx)
+ return chain_lib.dup(ret_chain_ctx)
+ elseif code == 0 then -- unverified
+ local vfy_code = C.X509_STORE_CTX_get_error(ctx)
+
+ return nil, ffi_str(C.X509_verify_cert_error_string(vfy_code))
+ end
+
+ -- error
+ return nil, format_error("x509.store:verify: X509_verify_cert", code)
+
+end
+
+return _M