diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a222dddd..6209890c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,47 +23,48 @@ jobs: runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: include: # TODO: arm64 - # latest and one version older for valgrind + # latest and one version older for valgrind and perf test - nginx: "1.19.9" openssl: "1.0.2u" - valgrind: "valgrind" + extras: "valgrind" lua_nginx_module: "v0.10.20" lua_resty_core: "v0.1.22" - nginx: "1.19.9" - openssl: "1.1.1s" - valgrind: "valgrind" + openssl: "1.1.1t" + extras: "valgrind" lua_nginx_module: "v0.10.20" lua_resty_core: "v0.1.22" - nginx: "1.19.9" openssl: "3.0.8" - valgrind: "valgrind" + extras: "valgrind" openssl_opts: "enable-fips" lua_nginx_module: "v0.10.20" lua_resty_core: "v0.1.22" nginx_cc_opts: "-Wno-error" - nginx: "1.21.4" openssl: "1.0.2u" - valgrind: "valgrind" + extras: "valgrind" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" - nginx: "1.21.4" - openssl: "1.1.1s" - valgrind: "valgrind" + openssl: "1.1.1t" + extras: "valgrind perf" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" - nginx: "1.21.4" openssl: "3.0.8" - valgrind: "valgrind" + extras: "valgrind perf" openssl_opts: "enable-fips" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" nginx_cc_opts: "-Wno-error" - nginx: "1.21.4" openssl: "3.1.0" - valgrind: "valgrind" + extras: "valgrind perf" openssl_opts: "enable-fips" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" @@ -78,17 +79,17 @@ jobs: openssl: "1.0.2u" fips2: "2.0.16" openssl_opts: "fips --with-fipsdir=/home/runner/work/cache/ssl/fips" - valgrind: "valgrind" + extras: "valgrind" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" - nginx: "1.21.4" boringssl: "ae223d6138807a13006342edfeef32e813246b39" # fips-20190808 - valgrind: "valgrind" + extras: "valgrind perf" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" - nginx: "1.21.4" boringssl: "853ca1ea1168dff08011e5d42d94609cc0ca2e27" # fips-20210429, not active yet - valgrind: "valgrind" + extras: "valgrind perf" lua_nginx_module: "v0.10.21" lua_resty_core: "v0.1.23" @@ -204,7 +205,7 @@ jobs: env: LUAJIT_CC_OPTS: ${{ matrix.luajit_cc_opts }} run: | - if [ "X${{ matrix.valgrind }}" != "X" ]; then LUAJIT_CC_OPTS="$LUAJIT_CC_OPTS -DLUAJIT_NUMMODE=2 -DLUAJIT_${{ matrix.valgrind }} -DLUAJIT_USE_SYSMALLOC -O0"; fi + if [[ "${{ matrix.extras }}" == *valgrind* ]]; then LUAJIT_CC_OPTS="$LUAJIT_CC_OPTS -DLUAJIT_NUMMODE=2 -DLUAJIT_USE_SYSMALLOC -O0"; fi export cd $LUAJIT_PREFIX if [ ! -e luajit2 ]; then git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git; fi @@ -221,7 +222,7 @@ jobs: env: NGINX_CC_OPTS: ${{ matrix.nginx_cc_opts }} run: | - if [ "X${{ matrix.valgrind }}" != "X" ]; then NGINX_CC_OPTS="$NGINX_CC_OPTS -O0"; fi + if [[ "${{ matrix.extras }}" == *valgrind* ]]; then NGINX_CC_OPTS="$NGINX_CC_OPTS -O0"; fi export PATH=$BASE_PATH/work/nginx/sbin:$BASE_PATH/../nginx-devel-utils:$PATH export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH export NGX_LUA_LOC=$BASE_PATH/../lua-nginx-module @@ -232,6 +233,20 @@ jobs: nginx -V ldd `which nginx`|grep -E 'luajit|ssl|pcre' + - name: Run performance test + if: contains(matrix.extras, 'perf') + run: | + wget https://github.com/openresty/resty-cli/raw/master/bin/resty + chmod +x resty + + export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH + export PATH=$BASE_PATH/work/nginx/sbin:$PATH + + for f in $(find examples/perf -type f -name "test_*" | sort); do + ./resty --no-stream -I lib $f + echo '================================================================' + done + - name: Run Test run: | export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH @@ -243,7 +258,7 @@ jobs: TEST_NGINX_TIMEOUT=10 prove -j$JOBS t/openssl/ssl/ 2>&1 - name: Run Valgrind - if: matrix.valgrind != '' + if: contains(matrix.extras, 'valgrind') run: | export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH export TEST_NGINX_VALGRIND='--num-callers=100 -q --tool=memcheck --leak-check=full --show-possibly-lost=no --gen-suppressions=all --suppressions=valgrind.suppress --track-origins=yes' TEST_NGINX_TIMEOUT=60 TEST_NGINX_SLEEP=1 diff --git a/examples/perf/framework.lua b/examples/perf/framework.lua new file mode 100644 index 00000000..eb22a284 --- /dev/null +++ b/examples/perf/framework.lua @@ -0,0 +1,93 @@ +local ffi = require "ffi" +local C = ffi.C +local ITER = 2000 + +local get_duration +do + ffi.cdef [[ + + typedef long time_t; + typedef int clockid_t; + typedef struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ + } nanotime; + + int clock_gettime(clockid_t clk_id, struct timespec *tp); + + ]] + local time_ns + do + local nanop = ffi.new("nanotime[1]") + function time_ns() + -- CLOCK_REALTIME -> 0 + C.clock_gettime(0, nanop) + local t = nanop[0] + + return tonumber(t.tv_sec) * 1e9 + tonumber(t.tv_nsec) + end + end + + local last = 0 + get_duration = function() + local n = time_ns() + local d = n - last + last = n + return d + end +end + +local function hmt(t) + if t > 1e9 * 0.01 then + return string.format("%.3f s", t/1e9) + elseif t > 1e6 * 0.01 then + return string.format("%.3f ms", t/1e6) + else + return string.format("%d ns", t) + end +end + +-- return sum, avg, max +local function stat(t) + if not t then + return 0, 0, 0 + end + + local v = 0 + local max = 0 + for _, i in ipairs(t) do + v = v + i + if i > max then + max = i + end + end + return v, v/#t, max +end + +local function test(desc, r, iter) + print("RUNNING " .. ITER .. " ITERATIONS FOR " .. desc) + local data = table.new(ITER, 0) + for i=1, ITER do + get_duration() + local ok, err = r() + data[i] = get_duration() + assert(ok, err) + end + + local sum, avg, max = stat(data) + + print(string.format("FINISHED in\t%s (%d op/s)\nAVG\t%s\tMAX\t%s", hmt(sum), 1e9/avg, hmt(avg), hmt(max))) + print(string.rep("-", 64)) +end + +local function set_iteration(i) + ITER = i +end + +print("LOADING TEST FROM " .. arg[0]) +print(string.rep("=", 64)) + +return { + test = test, + set_iteration = set_iteration, +} diff --git a/examples/perf/test_cipher.lua b/examples/perf/test_cipher.lua new file mode 100644 index 00000000..fc224566 --- /dev/null +++ b/examples/perf/test_cipher.lua @@ -0,0 +1,49 @@ +local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") +package.path = path .. "/?.lua;" .. package.path + +local test = require "framework".test +local set_iteration = require "framework".set_iteration +local cipher = require "resty.openssl.cipher" +local version = require("resty.openssl.version") + +local key = string.rep("0", 32) +local iv = string.rep("0", 16) +local data = string.rep("1", 4096) +local aad = string.rep("2", 10) + +set_iteration(100000) + +for _, t in ipairs({"aes-256-cbc", "aes-256-gcm", "chacha20-poly1305"}) do + for _, op in ipairs({"encrypt", "decrypt"}) do + -- the fips version of boringssl we used seems don't have chacha20 + if t == "chacha20-poly1305" and (not version.OPENSSL_111_OR_LATER or version.BORINGSSL) then + goto continue + end + + local c = assert(cipher.new(t)) + local _iv = iv + local _aad + if t == "aes-256-gcm" or t == "chacha20-poly1305" then + _iv = string.rep("0", 12) + _aad = aad + end + + if op == "encrypt" then + test("encrypt with " .. t .. " on " .. #data .. " bytes", function() + return c:encrypt(key, _iv, data, false, _aad) + end) + + else + local ciphertext = assert(c:encrypt(key, _iv, data, false, _aad)) + + local tag + if t == "aes-256-gcm" or t == "chacha20-poly1305" then + tag = assert(c:get_aead_tag()) + end + test("decrypt with " .. t .. " on " .. #ciphertext .. " bytes", function() + return c:decrypt(key, _iv, ciphertext, false, _aad, tag) + end) + end +::continue:: + end +end diff --git a/examples/perf/test_pkey_asymm.lua b/examples/perf/test_pkey_asymm.lua new file mode 100644 index 00000000..c153e7c4 --- /dev/null +++ b/examples/perf/test_pkey_asymm.lua @@ -0,0 +1,64 @@ +local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") +package.path = path .. "/?.lua;" .. package.path + +local test = require "framework".test +local set_iteration = require "framework".set_iteration +local pkey = require "resty.openssl.pkey" +local version = require("resty.openssl.version") +local data = string.rep("=", 200) + +set_iteration(1000) + +local rsa = pkey.new({ type = "RSA", bits = 4096 }) + +for _, op in ipairs({"encrypt", "decrypt"}) do + if op == "encrypt" then + test("encrypt with RSA on " .. #data .. " bytes", function() + return rsa:encrypt(data) + end) + + else + + local ciphertext = assert(rsa:encrypt(data)) + test("decrypt with RSA on " .. #ciphertext .. " bytes", function() + return rsa:decrypt(ciphertext) + end) + end +end + + +for _, t in ipairs({"RSA", "EC", "Ed25519", "Ed448"}) do + for _, op in ipairs({"sign", "verify"}) do + -- the fips version of boringssl we used seems don't have ed448 + if (t == "Ed25519" and not version.OPENSSL_111_OR_LATER) or (t == "Ed448" and version.BORINGSSL) then + goto continue + end + + local opts = { type = t } + if t == "EC" then + opts.curve = "prime256v1" + elseif t == "RSA" then + opts.bits = 4096 + end + + local c = assert(pkey.new(opts)) + local md_alg + if t == "RSA" or t == "EC" then + md_alg = "SHA256" + end + + if op == "sign" then + test("sign with " .. t .. (md_alg and ("-" .. md_alg) or "") .. " on " .. #data .. " bytes", function() + return c:sign(data, md_alg) + end) + + else + local sig = assert(c:sign(data, md_alg)) + + test("verify with " .. t .. (md_alg and ("-" .. md_alg) or "") .. " on " .. #data .. " bytes", function() + return c:verify(sig, data, md_alg) + end) + end +::continue:: + end +end diff --git a/examples/perf/test_pkey_codec.lua b/examples/perf/test_pkey_codec.lua new file mode 100644 index 00000000..973b8bb5 --- /dev/null +++ b/examples/perf/test_pkey_codec.lua @@ -0,0 +1,33 @@ +local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") +package.path = path .. "/?.lua;" .. package.path + +local test = require "framework".test +local pkey = require "resty.openssl.pkey" +local example_pkey = assert(pkey.new()) + +for _, op in ipairs({"load", "export"}) do + for _, t in ipairs({"PEM", "DER", "JWK"}) do + for _, p in ipairs({"public", "private"}) do + + if op == "load" then + local txt = assert(example_pkey:tostring(p, t)) + local opts = { + format = t, + } + if t ~= "JWK" then + opts.type = p == "public" and "pu" or "pr" + end + + test("load " .. t .. " " .. p .. " key", function() + return pkey.new(txt, opts) + end) + + else + test("export " .. t .. " " .. p .. " key", function() + return example_pkey:tostring(p, t) + end) + end + + end + end +end diff --git a/examples/perf/test_x509_codec.lua b/examples/perf/test_x509_codec.lua new file mode 100644 index 00000000..abc74df5 --- /dev/null +++ b/examples/perf/test_x509_codec.lua @@ -0,0 +1,23 @@ +local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") +package.path = path .. "/?.lua;" .. package.path + +local test = require "framework".test +local x509 = require "resty.openssl.x509" +local cert = assert(io.open(path .. "../../t/fixtures/Github.pem")):read("*a") +local example_x509 = assert(x509.new(cert)) + +for _, op in ipairs({"load", "export"}) do + for _, t in ipairs({"PEM", "DER"}) do + if op == "load" then + local txt = assert(example_x509:tostring(t)) + test("load " .. t .. " x509", function() + return x509.new(txt, t) + end) + + else + test("export " .. t .. " x509", function() + return example_x509:tostring(t) + end) + end + end +end