aboutsummaryrefslogtreecommitdiffstats
path: root/regress
diff options
context:
space:
mode:
Diffstat (limited to 'regress')
-rwxr-xr-xregress/00-store-verify.lua19
-rwxr-xr-xregress/104-interposition-discarded.lua12
-rwxr-xr-xregress/148-custom-extensions.lua57
-rwxr-xr-xregress/167-verify-cert.lua47
-rwxr-xr-xregress/177-reset-bio.lua35
-rwxr-xr-xregress/53-csr-extensions.lua170
-rwxr-xr-xregress/82-bn_prepops-null-deref.lua63
-rwxr-xr-xregress/95-kdf.lua45
-rw-r--r--regress/regress.lua210
9 files changed, 658 insertions, 0 deletions
diff --git a/regress/00-store-verify.lua b/regress/00-store-verify.lua
new file mode 100755
index 0000000..f45ad7e
--- /dev/null
+++ b/regress/00-store-verify.lua
@@ -0,0 +1,19 @@
+#!/usr/bin/env lua
+
+require"regress".export".*"
+
+local st = store.new()
+
+local ca_key, ca_crt = genkey()
+st:add(ca_crt)
+
+local key, crt = genkey("RSA", ca_key, ca_crt)
+
+local ok, proof_or_reason = st:verify(crt)
+check(ok, "%s", proof_or_reason)
+
+--for _,crt in pairs(proof_or_reason) do
+-- print(crt:text())
+--end
+
+say"OK"
diff --git a/regress/104-interposition-discarded.lua b/regress/104-interposition-discarded.lua
new file mode 100755
index 0000000..4261bcb
--- /dev/null
+++ b/regress/104-interposition-discarded.lua
@@ -0,0 +1,12 @@
+#!/usr/bin/env lua
+
+require"regress".export".*"
+
+local ssl_context = require "openssl.ssl.context"
+
+local value = {}
+ssl_context.interpose("foo", value)
+check(ssl_context.new().foo == value, "interpose failed")
+
+require "openssl.ssl" -- Pick a module that doesn't get loaded by regress.lua
+check(ssl_context.new().foo == value, "loading a module reset methods")
diff --git a/regress/148-custom-extensions.lua b/regress/148-custom-extensions.lua
new file mode 100755
index 0000000..110621c
--- /dev/null
+++ b/regress/148-custom-extensions.lua
@@ -0,0 +1,57 @@
+#!/usr/bin/env lua
+
+local regress = require "regress"
+local cqueues = require "cqueues"
+local cs = require "cqueues.socket"
+local openssl_ctx = require "openssl.ssl.context"
+
+local cli_ctx, srv_ctx
+local call_check = 0
+
+cli_ctx = regress.getsslctx("TLS", false, false)
+regress.check(cli_ctx.addCustomExtension, "Custom extension support not available")
+local function c_add_ext(ssl, ext_type, context) -- luacheck: ignore 212
+ call_check = call_check + 1
+ return "from the client"
+end
+local function c_parse_ext(ssl, ext_type, context, data) -- luacheck: ignore 212
+ call_check = call_check + 2
+ assert(data == "from the server")
+ return true
+end
+cli_ctx:addCustomExtension(5000,
+ openssl_ctx.EXT_CLIENT_HELLO +
+ openssl_ctx.EXT_TLS1_2_SERVER_HELLO +
+ openssl_ctx.EXT_TLS1_3_SERVER_HELLO
+, c_add_ext, c_parse_ext)
+
+
+srv_ctx = regress.getsslctx("TLS", true)
+local function s_add_ext(ssl, ext_type, context) -- luacheck: ignore 212
+ call_check = call_check + 4
+ return "from the server"
+end
+local function s_parse_ext(ssl, ext_type, context, data) -- luacheck: ignore 212
+ call_check = call_check + 8
+ assert(data == "from the client")
+ return true
+end
+srv_ctx:addCustomExtension(5000,
+ openssl_ctx.EXT_CLIENT_HELLO +
+ openssl_ctx.EXT_TLS1_2_SERVER_HELLO +
+ openssl_ctx.EXT_TLS1_3_SERVER_HELLO
+, s_add_ext, s_parse_ext)
+
+
+local srv, cli = regress.check(cs.pair(cs.SOCK_STREAM))
+local main = regress.check(cqueues.new())
+main:wrap(function ()
+ regress.check(cli:starttls(cli_ctx))
+end)
+main:wrap(function ()
+ regress.check(srv:starttls(srv_ctx))
+end)
+regress.check(main:loop())
+
+regress.check(call_check == 15, "callback count doesn't match")
+regress.say "OK"
diff --git a/regress/167-verify-cert.lua b/regress/167-verify-cert.lua
new file mode 100755
index 0000000..b7433e8
--- /dev/null
+++ b/regress/167-verify-cert.lua
@@ -0,0 +1,47 @@
+#!/usr/bin/env lua
+
+local regress = require "regress"
+
+if (regress.openssl.OPENSSL_VERSION_NUMBER and regress.openssl.OPENSSL_VERSION_NUMBER < 0x10002000)
+ or (regress.openssl.LIBRESSL_VERSION_NUMBER and regress.openssl.LIBRESSL_VERSION_NUMBER < 0x20705000)
+then
+ -- skipping test due to different behaviour in earlier OpenSSL versions
+ return
+end
+
+local params = regress.verify_param.new()
+params:setDepth(0)
+
+local ca_key, ca_crt = regress.genkey()
+do -- should fail as no trust anchor
+ regress.check(not ca_crt:verify({params=params, chain=nil, store=nil}))
+end
+
+local store = regress.store.new()
+store:add(ca_crt)
+do -- should succeed as cert is in the store
+ regress.check(ca_crt:verify({params=params, chain=nil, store=store}))
+end
+
+local intermediate_key, intermediate_crt = regress.genkey(nil, ca_key, ca_crt)
+do -- should succeed as ca cert is in the store
+ regress.check(intermediate_crt:verify({params=params, chain=nil, store=store}))
+end
+
+local _, crt = regress.genkey(nil, intermediate_key, intermediate_crt)
+do -- should fail as intermediate cert is missing
+ regress.check(not crt:verify({params=params, chain=nil, store=store}))
+end
+
+local chain = regress.chain.new()
+chain:add(intermediate_crt)
+do -- should fail as max depth is too low
+ regress.check(not crt:verify({params=params, chain=chain, store=store}))
+end
+
+params:setDepth(1)
+do -- should succeed
+ regress.check(crt:verify({params=params, chain=chain, store=store}))
+end
+
+regress.say "OK"
diff --git a/regress/177-reset-bio.lua b/regress/177-reset-bio.lua
new file mode 100755
index 0000000..565a511
--- /dev/null
+++ b/regress/177-reset-bio.lua
@@ -0,0 +1,35 @@
+#!/usr/bin/env lua
+
+local regress = require "regress"
+
+local ok, err
+
+local key = regress.pkey.new()
+
+-- generate a minimal certificate and export to DER
+local x509 = regress.x509.new()
+x509:setPublicKey(key)
+x509:sign(key)
+local x509_der = x509:tostring("DER")
+
+ok, err = pcall(regress.x509.new, x509_der)
+regress.check(ok, "failed to load DER certificate: %s", err)
+
+-- generate a minimal crl and export to DER
+local crl = regress.crl.new()
+crl:sign(key)
+local crl_der = crl:tostring("DER")
+
+ok, err = pcall(regress.crl.new, crl_der)
+regress.check(ok, "failed to load DER CRL: %s", err)
+
+-- generate a minimal csr and export to DER
+local csr = regress.csr.new()
+csr:setPublicKey(key)
+csr:sign(key)
+local csr_der = csr:tostring("DER")
+
+ok, err = pcall(regress.csr.new, csr_der)
+regress.check(ok, "failed to load DER CSR: %s", err)
+
+regress.say "OK"
diff --git a/regress/53-csr-extensions.lua b/regress/53-csr-extensions.lua
new file mode 100755
index 0000000..3d06b60
--- /dev/null
+++ b/regress/53-csr-extensions.lua
@@ -0,0 +1,170 @@
+#!/usr/bin/env lua
+
+local auxlib = require"openssl.auxlib"
+local pkey = require "openssl.pkey"
+local x509_csr = require"_openssl.x509.csr"
+local x509_altname = require"openssl.x509.altname"
+local x509_name = require"openssl.x509.name"
+
+local _basename = arg and arg[0] and arg[0]:match"([^/]+)$" or "UNKNOWN"
+
+local function cluck(fmt, ...)
+ io.stderr:write(_basename, ": ", string.format(fmt, ...), "\n")
+end
+
+local function croak(fmt, ...)
+ io.stderr:write(_basename, ": ", string.format(fmt, ...), "\n")
+ os.exit(1)
+end
+
+local function OK()
+ cluck("OK")
+ return true
+end
+
+local _testno = 0
+local function testnames(altnames, expected)
+ local matched = {}
+
+ _testno = _testno + 1
+
+ for type,data in auxlib.pairs(altnames) do
+ local found
+
+ for i,e in ipairs(expected) do
+ if not matched[i] and e.type == type and e.data == data then
+ cluck("expected match #%d.%d found (%s=%s)", _testno, i, type,data)
+
+ matched[i] = true
+ found = true
+ end
+ end
+
+ if not found then
+ return false, string.format("extra name in test #%d (%s=%s)", _testno, type, data)
+ end
+ end
+
+ for i,e in ipairs(expected) do
+ if not matched[i] then
+ return false, string.format("expected match #%d.%d not found (%s=%s)", _testno, i, e.type, e.data)
+ end
+ end
+
+ return true
+end
+
+local function checknames(altnames, expected)
+ local ok, why = testnames(altnames, expected)
+
+ if not ok then
+ croak(why or "UNKNOWN")
+ end
+
+ return true
+end
+
+key = pkey.new({ bits = 4096 })
+
+data = [[
+-----BEGIN CERTIFICATE REQUEST-----
+MIIFQjCCAyoCAQAwUzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRQwEgYDVQQH
+DAtNaW5uZWFwb2xpczEhMB8GA1UECwwYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVk
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4sXzE3GQtpFKiuGe389k
+MB0OaGXQxiI/yl6zm9PyYWe5aMpx1THDVhkWXemDVkduEqtLfa8GSNT0ps3BPdTx
+qxNwZ3J9xiVfNZZYO5ZSxs1g32M1lw20wIezLpbQ1ggyt01o9VTQDY6kA+D0G87B
+4FtIZxVaXM2z5HVaGYyivxAygDukDsO+RU0NC9mYOfAP4rt/u/xp8LsW0b4aIFqx
+gPcBZj92B+Wi2B4sKSe1m5kMfmh+e8v981hbY7V8FUMebB63iRGF6GU4kjXiMMW6
+gSoc+usq9li8VxjxPngql9pyLqFIa/2gW0c9sKKB2X9tB0nmudjAUrjZjHZEDlNr
+yx15JHhEIT31yP9xGQpy5H+jBldp/shqaV4Alsou9Hn9W71ap7VHOWLrIcaZGKTn
+CVSSYPygn4Rm8Cgwbv5mP6G+SqGHAFqirEysAARUFxsjBLlkNaVFOA38l2cufH8n
+1NE/B4iOG/ETvQDR/aKrbyKKo2k/hO941h3J9pwJcCikE0NsRcH6WAm8ifJ0Zd/q
+u8fqI8g9mYPbMWy11+njnfNOVFVhNOmM1/ZM66ac9zgGYncaHu4UzYnvWw75tDbF
+vA+oIJlcxBUtWeTcYRf4xEcRL8IcHEwh1BZq7bgP42Wu+0aBuaa3eYXNBApCNP39
+QmnHlo0iGH2rVeOfcq/wULcCAwEAAaCBqTCBpgYJKoZIhvcNAQkOMYGYMIGVMAkG
+A1UdEwQCMAAwCwYDVR0PBAQDAgXgMHsGA1UdEQR0MHKCE3NlcnZlcjEuZXhhbXBs
+ZS5jb22CEG1haWwuZXhhbXBsZS5jb22CD3d3dy5leGFtcGxlLmNvbYITd3d3LnN1
+Yi5leGFtcGxlLmNvbYIObXguZXhhbXBsZS5jb22CE3N1cHBvcnQuZXhhbXBsZS5j
+b20wDQYJKoZIhvcNAQEFBQADggIBAMiFPtDKVsy4HBhVkHSnbbIl41baaGGFjI/O
+MG8fI7P9jplq5rNZcLxSW2zLzMVuYzCoC+q5roRE5zVVyJlB+5dY0A8e2xKaWVOT
+AB9WvgepPvXDoGNViMBoX/idj3J2BU3e/cX08QWRPjKigwQWQWvUGsZYitGJv+Yv
+/LbIDlxr8Jr+1Txcm1EdXcff6Owlh6Nu59bgCMRdZvABmWfU5ULmUDTJnmc3P9St
+onz07v8ku8/XL7wwOfLJWVSVOk7RONySIJiPfVkgrU3YWiT64JaluDbFEIwnEgJS
+04xL6Pl66bADXCaeG3pZ8ypCs41+4bqFvCnOYma0Sk8fv8hSCWvJfMQI+nQslPJu
+UuGK4C4EEnYvoh/Qs/XEshfrVaNcG0zER3XtsRPAjhZjTPTcRgEjpOI0w3TJAvlN
+LSQV4mXN6E2bcU+cRYvNSgqITwJ7c6wpsONwApIQwFryLsFSCHaIdSLpAZbEPNEW
+UPa3uWXk5lWrBBPPkxyPbt8D3zpzahY4ycYEFKdz8MLdgA7pDalI2XpwgmoUybkw
+AJnsFg7fnFc03R4FsqxCqvbRYj3Bccb8Uhg1zTeXU+7nxjP2yYdT+In16L9SYOsU
+4ozEPqnGY9aI11i6C7hBwrUTvHYD6ZSDlylsUXKw/VZXQvS3+C0h6NuRmjBx8jNU
+RG1EyxL4
+-----END CERTIFICATE REQUEST-----
+]]
+
+-- baseline
+do
+ local expected = {
+ { type = "DNS", data = "server1.example.com" },
+ { type = "DNS", data = "mail.example.com" },
+ { type = "DNS", data = "www.example.com" },
+ { type = "DNS", data = "www.sub.example.com" },
+ { type = "DNS", data = "mx.example.com" },
+ { type = "DNS", data = "support.example.com" },
+ }
+
+ checknames((x509_csr.new(data)):getSubjectAlt(), expected)
+end
+
+-- modifying existing altnames
+do
+ local expected = {
+ { type = "DNS", data = "foo.com" },
+ { type = "DNS", data = "*.foo.com" },
+ }
+
+ local csr = x509_csr.new(data)
+ local gn = x509_altname.new()
+ gn:add("DNS", "foo.com")
+ gn:add("DNS", "*.foo.com")
+ csr:setSubjectAlt(gn)
+ csr:setPublicKey(key)
+ csr:sign(key)
+
+ -- check modified object
+ checknames(csr:getSubjectAlt(), expected)
+ -- check after a round-trip through PEM
+ checknames(x509_csr.new(tostring(csr)):getSubjectAlt(), expected)
+end
+
+-- adding altnames where none existed
+do
+ local expected = {
+ name = {
+ { type = "CN", data = "example.com" },
+ },
+ altname = {
+ { type = "DNS", data = "foo.com" },
+ { type = "DNS", data = "*.foo.com" },
+ },
+ }
+
+ local csr = x509_csr.new()
+ local name = x509_name.new()
+ name:add("CN", "example.com")
+ csr:setSubject(name)
+ local gn = x509_altname.new()
+ gn:add("DNS", "foo.com")
+ gn:add("DNS", "*.foo.com")
+ csr:setSubjectAlt(gn)
+ csr:setPublicKey(key)
+ csr:sign(key)
+
+ checknames(csr:getSubject(), expected.name)
+ checknames(csr:getSubjectAlt(), expected.altname)
+
+ local csr1 = x509_csr.new(tostring(csr))
+ checknames(csr1:getSubject(), expected.name)
+ checknames(csr1:getSubjectAlt(), expected.altname)
+end
+
+return OK()
+
diff --git a/regress/82-bn_prepops-null-deref.lua b/regress/82-bn_prepops-null-deref.lua
new file mode 100755
index 0000000..47757d2
--- /dev/null
+++ b/regress/82-bn_prepops-null-deref.lua
@@ -0,0 +1,63 @@
+#!/usr/bin/env lua
+--
+-- The following code could trigger a NULL dereference.
+--
+-- bn_prepops(lua_State *L, BIGNUM **r, BIGNUM **a, BIGNUM **b, _Bool commute) {
+-- ...
+-- *b = checkbig(L, 2, &lvalue);
+-- ...
+-- }
+--
+-- bn_sqr(lua_State *L) {
+-- BIGNUM *r, *a;
+--
+-- bn_prepops(L, &r, &a, NULL, 1);
+-- ...
+-- }
+--
+-- Caught by clang static analyzer. This was introduced with a patch adding
+-- the :sqr method. This should have been caught sooner as the :sqr method
+-- couldn't have possibly ever worked--a missing or non-numeric second
+-- operand would have thrown a Lua error, and a numeric second operand
+-- triggers the NULL dereference.
+--
+require"regress".export".*"
+
+local function N(i) return bignum.new(i) end
+
+-- passing a second numeric operand triggered a NULL dereference
+local r = N(4):sqr(0)
+
+
+-- check minimal functionality of all our operators
+local tests = {
+ { op = "add", a = 1, b = 1, r = 2 },
+ { op = "sub", a = 2, b = 1, r = 1 },
+ { op = "mul", a = 2, b = 2, r = 4 },
+ { op = "idiv", a = 4, b = 2, r = 2 },
+ { op = "mod", a = 4, b = 2, r = 0 },
+ { op = "exp", a = 2, b = 2, r = 4 },
+ { op = "sqr", a = 4, b = nil, r = 16 },
+ { op = "gcd", a = 47, b = 3, r = 1 },
+}
+
+local function tdescr(t)
+ return string.format("%s(%s, %s)", t.op, tostring(t.a), tostring(t.b))
+end
+
+for i,t in ipairs(tests) do
+ local a = N(t.a)
+ local op = a[t.op]
+ local ok, r
+
+ if t.b then
+ ok, r = pcall(op, a, t.b)
+ else
+ ok, r = pcall(op, a)
+ end
+
+ check(ok, "failed test #%d (%s) (%s)", i, tdescr(t), r)
+ check(N(r) == N(t.r), "failed test #%d (%s) (expected %s, got %s)", i, tdescr(t), tostring(t.r), tostring(r))
+end
+
+say"OK"
diff --git a/regress/95-kdf.lua b/regress/95-kdf.lua
new file mode 100755
index 0000000..dd7cff4
--- /dev/null
+++ b/regress/95-kdf.lua
@@ -0,0 +1,45 @@
+#!/usr/bin/env lua
+
+local regress = require "regress"
+local kdf = require "openssl.kdf"
+
+local function hexstring(str)
+ return (str:gsub("..", function(b) return string.char(tonumber(b, 16)) end))
+end
+
+-- Scrypt Example
+regress.check(kdf.derive{
+ type = "id-scrypt"; -- the nid short-name is id-scrypt
+ pass = "";
+ salt = "";
+ N = 16;
+ r = 1;
+ p = 1;
+ outlen = 64;
+} == hexstring"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
+ "scrypt output doesn't match test vector")
+
+-- PBKDF2 Example
+regress.check(kdf.derive{
+ type = "PBKDF2";
+ pass = "password";
+ salt = "salt";
+ iter = 1;
+ md = "sha1";
+ outlen = 20;
+} == hexstring"0c60c80f961f0e71f3a9b524af6012062fe037a6",
+ "PBKDF2 output doesn't match test vector")
+
+-- TLS1-PRF Example
+regress.check(kdf.derive{
+ type = "TLS1-PRF";
+ md = "md5-sha1";
+ secret = hexstring"bded7fa5c1699c010be23dd06ada3a48349f21e5f86263d512c0c5cc379f0e780ec55d9844b2f1db02a96453513568d0";
+ seed = "master secret"
+ .. hexstring"e5acaf549cd25c22d964c0d930fa4b5261d2507fad84c33715b7b9a864020693"
+ .. hexstring"135e4d557fdf3aa6406d82975d5c606a9734c9334b42136e96990fbd5358cdb2";
+ outlen = 48;
+} == hexstring"2f6962dfbc744c4b2138bb6b3d33054c5ecc14f24851d9896395a44ab3964efc2090c5bf51a0891209f46c1e1e998f62",
+ "TLS1-PRF output doesn't match test vector")
+
+regress.say "OK"
diff --git a/regress/regress.lua b/regress/regress.lua
new file mode 100644
index 0000000..044e342
--- /dev/null
+++ b/regress/regress.lua
@@ -0,0 +1,210 @@
+local require = require -- may be overloaded by regress.require
+
+local regress = {
+ openssl = require"openssl",
+ bignum = require"openssl.bignum",
+ kdf = require"openssl.kdf",
+ pkey = require"openssl.pkey",
+ x509 = require"openssl.x509",
+ name = require"openssl.x509.name",
+ altname = require"openssl.x509.altname",
+ chain = require"openssl.x509.chain",
+ store = require"openssl.x509.store",
+ verify_param = require"openssl.x509.verify_param",
+ crl = require"openssl.x509.crl",
+ csr = require"openssl.x509.csr",
+ pack = table.pack or function (...)
+ local t = { ... }
+ t.n = select("#", ...)
+ return t
+ end,
+ unpack = table.unpack or unpack,
+}
+
+local emit_progname = os.getenv"REGRESS_PROGNAME" or "regress"
+local emit_verbose = tonumber(os.getenv"REGRESS_VERBOSE" or 1)
+local emit_info = {}
+local emit_ll = 0
+
+local function emit(fmt, ...)
+ local msg = string.format(fmt, ...)
+
+ for txt, nl in msg:gmatch("([^\n]*)(\n?)") do
+ if emit_ll == 0 and #txt > 0 then
+ io.stderr:write(emit_progname, ": ")
+ emit_ll = #emit_progname + 2
+ end
+
+ io.stderr:write(txt, nl)
+
+ if nl == "\n" then
+ emit_ll = 0
+ else
+ emit_ll = emit_ll + #txt
+ end
+ end
+end -- emit
+
+local function emitln(fmt, ...)
+ if emit_ll > 0 then
+ emit"\n"
+ end
+
+ emit(fmt .. "\n", ...)
+end -- emitln
+
+local function emitinfo()
+ for _, txt in ipairs(emit_info) do
+ emitln("%s", txt)
+ end
+end -- emitinfo
+
+function regress.say(...)
+ emitln(...)
+end -- say
+
+function regress.panic(...)
+ emitinfo()
+ emitln(...)
+ os.exit(1)
+end -- panic
+
+function regress.info(...)
+ if emit_verbose > 1 then
+ emitln(...)
+ else
+ emit_info[#emit_info + 1] = string.format(...)
+
+ if emit_verbose > 0 then
+ if emit_ll > 78 then
+ emit"\n."
+ else
+ emit"."
+ end
+ end
+ end
+end -- info
+
+function regress.check(v, ...)
+ if v then
+ return v, ...
+ else
+ regress.panic(...)
+ end
+end -- check
+
+function regress.export(...)
+ for _, pat in ipairs{ ... } do
+ for k, v in pairs(regress) do
+ if string.match(k, pat) then
+ _G[k] = v
+ end
+ end
+ end
+
+ return regress
+end -- export
+
+function regress.require(modname)
+ local ok, module = pcall(require, modname)
+
+ regress.check(ok, "module %s required", modname)
+
+ return module
+end -- regress.require
+
+local counter = 0
+function regress.genkey(type, ca_key, ca_crt)
+ local pkey = regress.require"openssl.pkey"
+ local x509 = regress.require"openssl.x509"
+ local name = regress.require"openssl.x509.name"
+ local altname = regress.require"openssl.x509.altname"
+ local key
+
+ type = string.upper(type or "RSA")
+
+ if type == "EC" then
+ key = regress.check(pkey.new{ type = "EC", curve = "prime192v1" })
+ else
+ key = regress.check(pkey.new{ type = type, bits = 1024 })
+ end
+
+ local dn = name.new()
+ dn:add("C", "US")
+ dn:add("ST", "California")
+ dn:add("L", "San Francisco")
+ dn:add("O", "Acme, Inc.")
+ dn:add("CN", string.format("acme%d.inc", counter))
+ counter = counter + 1
+
+ local alt = altname.new()
+ alt:add("DNS", "acme.inc")
+ alt:add("DNS", "localhost")
+
+ local crt = x509.new()
+ crt:setVersion(3)
+ crt:setSerial(47)
+ crt:setSubject(dn)
+ crt:setIssuer((ca_crt or crt):getSubject())
+ crt:setSubjectAlt(alt)
+
+ local issued, expires = crt:getLifetime()
+ crt:setLifetime(issued, expires + 60)
+
+ crt:setBasicConstraints{ CA = true, pathLen = 2 }
+ crt:setBasicConstraintsCritical(true)
+
+ crt:setPublicKey(key)
+ crt:sign(ca_key or key)
+
+ return key, crt
+end -- regress.genkey
+
+local function getsubtable(t, name, ...)
+ name = name or false -- cannot be nil
+
+ if not t[name] then
+ t[name] = {}
+ end
+
+ if select('#', ...) > 0 then
+ return getsubtable(t[name], ...)
+ else
+ return t[name]
+ end
+end -- getsubtable
+
+function regress.newsslctx(protocol, accept, keytype)
+ local context = regress.require"openssl.ssl.context"
+ local ctx = context.new(protocol, accept)
+
+ if keytype or keytype == nil then
+ local key, crt = regress.genkey(keytype)
+
+ ctx:setCertificate(crt)
+ ctx:setPrivateKey(key)
+ end
+
+ return ctx
+end -- require.newsslctx
+
+local ctxcache = {}
+
+function regress.getsslctx(protocol, accept, keytype)
+ local keycache = getsubtable(ctxcache, protocol, accept)
+
+ if keytype == nil then
+ keytype = "RSA"
+ end
+
+ local ctx = keycache[keytype]
+
+ if not ctx then
+ ctx = regress.newsslctx(protocol, accept, keytype)
+ keycache[keytype] = ctx
+ end
+
+ return ctx
+end -- regress.getsslctx
+
+return regress