diff options
Diffstat (limited to 'server/resty/openssl/x509/crl.lua')
-rw-r--r-- | server/resty/openssl/x509/crl.lua | 607 |
1 files changed, 607 insertions, 0 deletions
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 + |