aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/luaossl.tex13
-rwxr-xr-xregress/167-verify-cert.lua47
-rw-r--r--regress/regress.lua2
-rw-r--r--src/openssl.c120
4 files changed, 182 insertions, 0 deletions
diff --git a/doc/luaossl.tex b/doc/luaossl.tex
index 8561957..b874e89 100644
--- a/doc/luaossl.tex
+++ b/doc/luaossl.tex
@@ -597,6 +597,19 @@ Returns the type of signature used to sign the certificate as a string. e.g. ``R
Signs and updates the instance certificate using the \module{openssl.pkey} $key$. $type$ is an optional string describing the digest type. See \module{pkey:sign}, regarding which types of digests are valid. If $type$ is omitted than a default type is used---``sha1'' for RSA keys, ``dss1'' for DSA keys, and ``ecdsa-with-SHA1'' for EC keys.
+\subsubsection[\fn{x509:verify}]{\fn{x509:verify\{ $\ldots$ \}}}
+
+Verifies the certificate against to the specified parameters.
+
+\begin{ctabular}{ c | c | p{9cm}}
+field & type & description\\\hline
+.store & \module{openssl.x509.store} & The certificate store to verify against, any custom settings from the store will be used. \\
+.chain & \module{openssl.x509.chain} & A collection of additional certificates to consider \\
+.params & \module{openssl.x509.verify\_param} & The verification parameters to use; overrides any parameters in $.store$
+\end{ctabular}
+
+Returns two values. The first is a boolean value for whether the specified certificate $crt$ was verified. If true, the second value is a \module{openssl.x509.chain} object validation chain. If false, the second value is a string describing why verification failed.
+
\subsubsection[\fn{x509:text}]{\fn{x509:text()}}
Returns a human-readable textual representation of the X.509 certificate.
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/regress.lua b/regress/regress.lua
index 19ee065..5cdd22d 100644
--- a/regress/regress.lua
+++ b/regress/regress.lua
@@ -8,7 +8,9 @@ local regress = {
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",
pack = table.pack or function (...)
local t = { ... }
t.n = select("#", ...)
diff --git a/src/openssl.c b/src/openssl.c
index 9e0447f..ffb34d6 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -7160,6 +7160,125 @@ static int xc_sign(lua_State *L) {
} /* xc_sign() */
+static int xc_verify(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_STORE *store = NULL;
+ STACK_OF(X509) *chain = NULL;
+ X509_VERIFY_PARAM *params = NULL;
+ X509_STORE_CTX *ctx = NULL;
+ int ok, why;
+ STACK_OF(X509) **proof;
+
+ if (lua_istable(L, 2)) {
+ if (lua_getfield(L, 2, "store") != LUA_TNIL) {
+ store = checksimple(L, -1, X509_STORE_CLASS);
+ } else if (!(OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,5))) {
+ /*
+ Without .store OpenSSL 1.0.1 crashes e.g.
+
+ #0 X509_STORE_get_by_subject (vs=vs@entry=0x6731b0, type=type@entry=1, name=name@entry=0x66a360, ret=ret@entry=0x7fffffffe580) at x509_lu.c:293
+ #1 0x00007ffff69653ca in X509_STORE_CTX_get1_issuer (issuer=0x7fffffffe620, ctx=0x6731b0, x=0x665db0) at x509_lu.c:604
+ #2 0x00007ffff696117c in X509_verify_cert (ctx=ctx@entry=0x6731b0) at x509_vfy.c:256
+
+ Was fixed in LibreSSL somewhere between 2.6.5 and 2.7.5
+ */
+ luaL_argerror(L, 2, ".store required in OpenSSL <= 1.0.1");
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 2, "chain") != LUA_TNIL) {
+ chain = checksimple(L, -1, X509_CHAIN_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 2, "params") != LUA_TNIL) {
+ params = checksimple(L, -1, X509_VERIFY_PARAM_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 2, "crls") != LUA_TNIL) {
+ luaL_argerror(L, 2, "crls not yet supported");
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 2, "dane") != LUA_TNIL) {
+ luaL_argerror(L, 2, "dane not yet supported");
+ }
+ lua_pop(L, 1);
+ }
+
+ /* pre-allocate space for a successful return */
+ proof = prepsimple(L, X509_CHAIN_CLASS);
+
+ if (chain) {
+ X509 *elm;
+ int i, n;
+
+ if (!(chain = sk_X509_dup(chain)))
+ goto eossl;
+
+ n = sk_X509_num(chain);
+
+ for (i = 0; i < n; i++) {
+ if (!(elm = sk_X509_value(chain, i)))
+ continue;
+ X509_up_ref(elm);
+ }
+ }
+
+ if (!(ctx = X509_STORE_CTX_new()) || !X509_STORE_CTX_init(ctx, store, crt, chain)) {
+ sk_X509_pop_free(chain, X509_free);
+ goto eossl;
+ }
+
+ if (params) {
+ X509_VERIFY_PARAM *params_copy = X509_VERIFY_PARAM_new();
+ if (!params_copy)
+ goto eossl;
+
+ ok = X509_VERIFY_PARAM_inherit(params_copy, params);
+ if (!ok) {
+ X509_VERIFY_PARAM_free(params_copy);
+ goto eossl;
+ }
+
+ X509_STORE_CTX_set0_param(ctx, params_copy);
+ }
+
+ ERR_clear_error();
+
+ ok = X509_verify_cert(ctx);
+
+ switch (ok) {
+ case 1: /* verified */
+ if (!(*proof = X509_STORE_CTX_get1_chain(ctx)))
+ goto eossl;
+ X509_STORE_CTX_free(ctx);
+
+ lua_pushboolean(L, 1);
+ lua_pushvalue(L, -2);
+
+ return 2;
+ case 0: /* not verified */
+ why = X509_STORE_CTX_get_error(ctx);
+ X509_STORE_CTX_free(ctx);
+
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, X509_verify_cert_error_string(why));
+
+ return 2;
+ default:
+ goto eossl;
+ }
+
+eossl:
+ if (ctx)
+ X509_STORE_CTX_free(ctx);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:verify");
+} /* xc_verify() */
+
+
static int xc_text(lua_State *L) {
static const struct { const char *kw; unsigned int flag; } map[] = {
{ "no_header", X509_FLAG_NO_HEADER },
@@ -7311,6 +7430,7 @@ static const auxL_Reg xc_methods[] = {
{ "getPublicKeyDigest", &xc_getPublicKeyDigest },
{ "getSignatureName", &xc_getSignatureName },
{ "sign", &xc_sign },
+ { "verify", &xc_verify },
{ "text", &xc_text },
{ "toPEM", &xc_toPEM },
{ "tostring", &xc__tostring },