diff options
Diffstat (limited to 'regress')
-rwxr-xr-x | regress/00-store-verify.lua | 19 | ||||
-rwxr-xr-x | regress/104-interposition-discarded.lua | 12 | ||||
-rwxr-xr-x | regress/148-custom-extensions.lua | 57 | ||||
-rwxr-xr-x | regress/167-verify-cert.lua | 47 | ||||
-rwxr-xr-x | regress/177-reset-bio.lua | 35 | ||||
-rwxr-xr-x | regress/53-csr-extensions.lua | 170 | ||||
-rwxr-xr-x | regress/82-bn_prepops-null-deref.lua | 63 | ||||
-rwxr-xr-x | regress/95-kdf.lua | 45 | ||||
-rw-r--r-- | regress/regress.lua | 210 |
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 |