From f0462892479ac73e89961902084ab1c7bd61d034 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 2 Sep 2018 13:07:20 +0300 Subject: [PATCH 1/9] pkey.toPEM: opts: rename const array --- src/openssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index db64d03..c3114b7 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4718,14 +4718,14 @@ static int pk_toPEM(lua_State *L) { bio = getbio(L); for (i = 2; i <= top; i++) { - static const char *const opts[] = { + static const char *const types[] = { "public", "PublicKey", "private", "PrivateKey", // "params", "Parameters", NULL, }; - switch (auxL_checkoption(L, i, NULL, opts, 1)) { + switch (auxL_checkoption(L, i, NULL, types, 1)) { case 0: case 1: /* public, PublicKey */ if (!PEM_write_bio_PUBKEY(bio, key)) return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring"); From 8d065b2d3970ebe22f7be1f8b04779c94e562775 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 2 Sep 2018 13:18:48 +0300 Subject: [PATCH 2/9] pkey.toPEM: accept table arguments --- src/openssl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/openssl.c b/src/openssl.c index c3114b7..36fdd28 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4724,8 +4724,17 @@ static int pk_toPEM(lua_State *L) { // "params", "Parameters", NULL, }; + int type; - switch (auxL_checkoption(L, i, NULL, types, 1)) { + if (!lua_istable(L, i)) + lua_pushvalue(L, i); + else if (!getfield(L, i, "type")) + lua_pushliteral(L, "public"); + + type = auxL_checkoption(L, -1, NULL, types, 1); + lua_pop(L, 1); + + switch (type) { case 0: case 1: /* public, PublicKey */ if (!PEM_write_bio_PUBKEY(bio, key)) return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring"); From 15f50f126b152de53b4c91cd6e0dad818ff45043 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Mon, 30 Apr 2018 13:26:16 +0300 Subject: [PATCH 3/9] pkey.toPEM: private key encryption --- src/openssl.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index 36fdd28..f5426bf 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -31,7 +31,7 @@ #include /* INT_MAX INT_MIN LLONG_MAX LLONG_MIN UCHAR_MAX ULLONG_MAX */ #include /* uintptr_t */ -#include /* memset(3) strerror_r(3) */ +#include /* memset(3) strerror_r(3) strlen(3) */ #include /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ #include /* struct tm time_t strptime(3) time(2) */ #include /* isdigit(3), isxdigit(3), tolower(3) */ @@ -4725,11 +4725,16 @@ static int pk_toPEM(lua_State *L) { NULL, }; int type; - - if (!lua_istable(L, i)) + const char *cname = NULL; + const EVP_CIPHER *cipher = NULL; + const char *pass = NULL; + + if (lua_istable(L, i)) { + loadfield(L, i, "cipher", LUA_TSTRING, &cname); + if (!getfield(L, i, "type")) + lua_pushstring(L, cname ? "private" : "public"); + } else lua_pushvalue(L, i); - else if (!getfield(L, i, "type")) - lua_pushliteral(L, "public"); type = auxL_checkoption(L, -1, NULL, types, 1); lua_pop(L, 1); @@ -4745,7 +4750,15 @@ static int pk_toPEM(lua_State *L) { break; case 2: case 3: /* private, PrivateKey */ - if (!PEM_write_bio_PrivateKey(bio, key, 0, 0, 0, 0, 0)) + if (cname) { + cipher = EVP_get_cipherbyname(cname); + if (!cipher) + return luaL_error(L, "pkey:toPEM: unknown cipher: %s", cname); + if (!loadfield(L, i, "password", LUA_TSTRING, &pass)) + return luaL_error(L, "pkey:toPEM: password not defined"); + } + + if (!PEM_write_bio_PrivateKey(bio, key, cipher, pass, pass ? strlen(pass) : 0, 0, 0)) return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring"); len = BIO_get_mem_data(bio, &pem); From 6e003ece5b8eacd38f77b2a3abdfdf508442f3fd Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 2 Sep 2018 14:22:43 +0300 Subject: [PATCH 4/9] pkey.new: type: rename variable --- src/openssl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index f5426bf..c8324d7 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4342,7 +4342,7 @@ static int pk_new(lua_State *L) { #endif } /* switch() */ } else if (lua_isstring(L, 1)) { - int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); + int format = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); int pubonly = 0, prvtonly = 0; const char *opt, *data; size_t len; @@ -4368,7 +4368,7 @@ static int pk_new(lua_State *L) { if (!(bio = BIO_new_mem_buf((void *)data, len))) return auxL_error(L, auxL_EOPENSSL, "pkey.new"); - if (type == X509_PEM || type == X509_ANY) { + if (format == X509_PEM || format == X509_ANY) { if (!prvtonly && !pub) { /* * BIO_reset is a rewind for read-only @@ -4389,7 +4389,7 @@ static int pk_new(lua_State *L) { } } - if (type == X509_DER || type == X509_ANY) { + if (format == X509_DER || format == X509_ANY) { if (!prvtonly && !pub) { BIO_reset(bio); From 6973ec60f7ce97396b9f4bedb7408a102c866abf Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 2 Sep 2018 14:24:51 +0300 Subject: [PATCH 5/9] pkey.new: opt: rename variable --- src/openssl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index c8324d7..9ab7142 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4344,20 +4344,20 @@ static int pk_new(lua_State *L) { } else if (lua_isstring(L, 1)) { int format = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); int pubonly = 0, prvtonly = 0; - const char *opt, *data; + const char *type, *data; size_t len; BIO *bio; EVP_PKEY *pub = NULL, *prvt = NULL; int goterr = 0; /* check if specified publickey or privatekey */ - if ((opt = luaL_optstring(L, 3, NULL))) { - if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'u') { + if ((type = luaL_optstring(L, 3, NULL))) { + if (xtolower(type[0]) == 'p' && xtolower(type[1]) == 'u') { pubonly = 1; - } else if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'r') { + } else if (xtolower(type[0]) == 'p' && xtolower(type[1]) == 'r') { prvtonly = 1; } else { - return luaL_argerror(L, 3, lua_pushfstring(L, "invalid option %s", opt)); + return luaL_argerror(L, 3, lua_pushfstring(L, "invalid type: %s", type)); } } From 48a9f0cc05052a827759dbef768bb82daf6ccdaa Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 2 Sep 2018 14:44:46 +0300 Subject: [PATCH 6/9] pkey.new: accept option table --- src/openssl.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index 9ab7142..beda03d 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4108,7 +4108,7 @@ static BIO *getbio(lua_State *L) { static int pk_new(lua_State *L) { EVP_PKEY **ud; - /* #1 table or key; if key, #2 format and #3 type */ + /* #1 table or key; if key, #2 option table or format; if format, #3 type */ lua_settop(L, 3); if (lua_istable(L, 1) || lua_isnil(L, 1)) { @@ -4342,7 +4342,7 @@ static int pk_new(lua_State *L) { #endif } /* switch() */ } else if (lua_isstring(L, 1)) { - int format = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); + int format; int pubonly = 0, prvtonly = 0; const char *type, *data; size_t len; @@ -4350,6 +4350,18 @@ static int pk_new(lua_State *L) { EVP_PKEY *pub = NULL, *prvt = NULL; int goterr = 0; + if (lua_istable(L, 2)) { + lua_pop(L, 1); + lua_getfield(L, 2, "format"); + lua_getfield(L, 2, "type"); + lua_remove(L, 2); + } + + /* #1 key, #2 format, #3 type */ + + data = luaL_checklstring(L, 1, &len); + format = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); + /* check if specified publickey or privatekey */ if ((type = luaL_optstring(L, 3, NULL))) { if (xtolower(type[0]) == 'p' && xtolower(type[1]) == 'u') { @@ -4357,12 +4369,10 @@ static int pk_new(lua_State *L) { } else if (xtolower(type[0]) == 'p' && xtolower(type[1]) == 'r') { prvtonly = 1; } else { - return luaL_argerror(L, 3, lua_pushfstring(L, "invalid type: %s", type)); + return luaL_error(L, "invalid key type: %s", type); } } - data = luaL_checklstring(L, 1, &len); - ud = prepsimple(L, PKEY_CLASS); if (!(bio = BIO_new_mem_buf((void *)data, len))) From 0ac6b9f3b5ef74221caa85a8b6fd01411b541ad2 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Mon, 30 Apr 2018 13:49:57 +0300 Subject: [PATCH 7/9] pkey.new: decryption --- src/openssl.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index beda03d..b55304e 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -31,7 +31,7 @@ #include /* INT_MAX INT_MIN LLONG_MAX LLONG_MIN UCHAR_MAX ULLONG_MAX */ #include /* uintptr_t */ -#include /* memset(3) strerror_r(3) strlen(3) */ +#include /* memset(3) strerror_r(3) strlen(3) strncpy(3) */ #include /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ #include /* struct tm time_t strptime(3) time(2) */ #include /* isdigit(3), isxdigit(3), tolower(3) */ @@ -4105,6 +4105,15 @@ static BIO *getbio(lua_State *L) { } /* getbio() */ +static int pem_pw_cb(char *buf, int size, int rwflag, void *u) { + if (!u) + return -1; + char *pass = (char *) u; + strncpy(buf, pass, size); + return MIN(strlen(pass), (unsigned int) size); +} /* pem_pw_cb() */ + + static int pk_new(lua_State *L) { EVP_PKEY **ud; @@ -4344,7 +4353,7 @@ static int pk_new(lua_State *L) { } else if (lua_isstring(L, 1)) { int format; int pubonly = 0, prvtonly = 0; - const char *type, *data; + const char *type, *data, *pass; size_t len; BIO *bio; EVP_PKEY *pub = NULL, *prvt = NULL; @@ -4354,10 +4363,12 @@ static int pk_new(lua_State *L) { lua_pop(L, 1); lua_getfield(L, 2, "format"); lua_getfield(L, 2, "type"); + lua_getfield(L, 2, "password"); lua_remove(L, 2); - } + } else + lua_pushnil(L); - /* #1 key, #2 format, #3 type */ + /* #1 key, #2 format, #3 type, #4 password or callback */ data = luaL_checklstring(L, 1, &len); format = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); @@ -4373,6 +4384,13 @@ static int pk_new(lua_State *L) { } } + pass = luaL_optstring(L, 4, NULL); + if (pass) { + if (format == X509_DER) + return luaL_error(L, "decryption supported only for PEM keys"); + else format = X509_PEM; + } + ud = prepsimple(L, PKEY_CLASS); if (!(bio = BIO_new_mem_buf((void *)data, len))) @@ -4387,14 +4405,14 @@ static int pk_new(lua_State *L) { */ BIO_reset(bio); - if (!(pub = PEM_read_bio_PUBKEY(bio, NULL, 0, ""))) + if (!(pub = PEM_read_bio_PUBKEY(bio, NULL, pem_pw_cb, pass))) goterr = 1; } if (!pubonly && !prvt) { BIO_reset(bio); - if (!(prvt = PEM_read_bio_PrivateKey(bio, NULL, 0, ""))) + if (!(prvt = PEM_read_bio_PrivateKey(bio, NULL, pem_pw_cb, pass))) goterr = 1; } } From 66cb08749ab33660b4d98c22c31f9184cf2f209e Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Thu, 3 May 2018 21:21:16 +0300 Subject: [PATCH 8/9] pkey.toPEM: use password callback --- src/openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openssl.c b/src/openssl.c index b55304e..52e28bf 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4786,7 +4786,7 @@ static int pk_toPEM(lua_State *L) { return luaL_error(L, "pkey:toPEM: password not defined"); } - if (!PEM_write_bio_PrivateKey(bio, key, cipher, pass, pass ? strlen(pass) : 0, 0, 0)) + if (!PEM_write_bio_PrivateKey(bio, key, cipher, NULL, 0, pem_pw_cb, pass)) return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring"); len = BIO_get_mem_data(bio, &pem); From aeb4f95490fea3ad68a33201ef08e39b906dfd23 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Thu, 3 May 2018 21:37:30 +0300 Subject: [PATCH 9/9] pkey: PEM password callback --- src/openssl.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index 52e28bf..30f3ae9 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -4105,10 +4105,28 @@ static BIO *getbio(lua_State *L) { } /* getbio() */ +/* + * PEM password callback for openssl + * + * Expects nil, string, or function on top of the stack. Leaves one item on the + * top of the stack: the original string, the string returned by the function, + * or nil (in case of an error or missing password). + * + * This callback may be called twice by pk_new when the PEM key is encrypted and + * its type is not specified. The user-provided function is called only once + * because it gets replaced on the stack by the return value. This callback may + * not be called at all if the supplied PEM key is not encrypted. + */ static int pem_pw_cb(char *buf, int size, int rwflag, void *u) { - if (!u) + lua_State *L = (lua_State *) u; + + if (lua_isfunction(L, -1) && lua_pcall(L, 0, 1, 0) || !lua_isstring(L, -1)) { + lua_pop(L, 1); + lua_pushnil(L); return -1; - char *pass = (char *) u; + } + + const char *pass = lua_tostring(L, -1); strncpy(buf, pass, size); return MIN(strlen(pass), (unsigned int) size); } /* pem_pw_cb() */ @@ -4353,7 +4371,7 @@ static int pk_new(lua_State *L) { } else if (lua_isstring(L, 1)) { int format; int pubonly = 0, prvtonly = 0; - const char *type, *data, *pass; + const char *type, *data; size_t len; BIO *bio; EVP_PKEY *pub = NULL, *prvt = NULL; @@ -4384,8 +4402,7 @@ static int pk_new(lua_State *L) { } } - pass = luaL_optstring(L, 4, NULL); - if (pass) { + if (!lua_isnil(L, 4)) { if (format == X509_DER) return luaL_error(L, "decryption supported only for PEM keys"); else format = X509_PEM; @@ -4397,6 +4414,8 @@ static int pk_new(lua_State *L) { return auxL_error(L, auxL_EOPENSSL, "pkey.new"); if (format == X509_PEM || format == X509_ANY) { + lua_pushvalue(L, 4); + if (!prvtonly && !pub) { /* * BIO_reset is a rewind for read-only @@ -4405,16 +4424,18 @@ static int pk_new(lua_State *L) { */ BIO_reset(bio); - if (!(pub = PEM_read_bio_PUBKEY(bio, NULL, pem_pw_cb, pass))) + if (!(pub = PEM_read_bio_PUBKEY(bio, NULL, pem_pw_cb, L))) goterr = 1; } if (!pubonly && !prvt) { BIO_reset(bio); - if (!(prvt = PEM_read_bio_PrivateKey(bio, NULL, pem_pw_cb, pass))) + if (!(prvt = PEM_read_bio_PrivateKey(bio, NULL, pem_pw_cb, L))) goterr = 1; } + + lua_pop(L, 1); } if (format == X509_DER || format == X509_ANY) { @@ -4755,7 +4776,6 @@ static int pk_toPEM(lua_State *L) { int type; const char *cname = NULL; const EVP_CIPHER *cipher = NULL; - const char *pass = NULL; if (lua_istable(L, i)) { loadfield(L, i, "cipher", LUA_TSTRING, &cname); @@ -4782,13 +4802,17 @@ static int pk_toPEM(lua_State *L) { cipher = EVP_get_cipherbyname(cname); if (!cipher) return luaL_error(L, "pkey:toPEM: unknown cipher: %s", cname); - if (!loadfield(L, i, "password", LUA_TSTRING, &pass)) + if (!getfield(L, i, "password")) return luaL_error(L, "pkey:toPEM: password not defined"); } + else + lua_pushnil(L); - if (!PEM_write_bio_PrivateKey(bio, key, cipher, NULL, 0, pem_pw_cb, pass)) + if (!PEM_write_bio_PrivateKey(bio, key, cipher, NULL, 0, pem_pw_cb, L)) return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring"); + lua_pop(L, 1); + len = BIO_get_mem_data(bio, &pem); lua_pushlstring(L, pem, len); BIO_reset(bio);