Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for encrypted PEM keys #128

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

kunkku
Copy link
Contributor

@kunkku kunkku commented May 3, 2018

No description provided.

src/openssl.c Outdated Show resolved Hide resolved
src/openssl.c Outdated Show resolved Hide resolved
src/openssl.c Outdated Show resolved Hide resolved
src/openssl.c Outdated Show resolved Hide resolved
@kunkku
Copy link
Contributor Author

kunkku commented Jul 2, 2018

Please let me know if further changes are required to this patch set.

Copy link
Collaborator

@daurnimator daurnimator left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of technical issues spotted; I still need to do a more thorough review.

src/openssl.c Show resolved Hide resolved
src/openssl.c Outdated
@@ -3674,6 +3687,8 @@ static int pk_new(lua_State *L) {
}
}

ud = prepsimple(L, PKEY_CLASS);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can throw and leak bio (which is why it was previously done before allocating bio)

src/openssl.c Outdated Show resolved Hide resolved
src/openssl.c Show resolved Hide resolved
src/openssl.c Outdated
char *pass = (char *) u;
strncpy(buf, pass, size);
return MIN(strlen(pass), (unsigned int) size);
} /* pem_password_cb() */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there is a typo in the comment. Should be /* pem_pw_cb() */

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs fixing

src/openssl.c Outdated
lua_settop(L, 3);

if (cname) {
cipher = EVP_get_cipherbyname(cname);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives a warning:

src/openssl.c: In functionpk_getPrivateKey’:
src/openssl.c:4132:10: warning: assignment discardsconstqualifier from pointer target type [-Wdiscarded-qualifiers]
   cipher = EVP_get_cipherbyname(cname);
          ^

src/openssl.c Outdated Show resolved Hide resolved
@daurnimator
Copy link
Collaborator

So this PR works, but I've found it very odd to use.

e.g. here is a valid invocation:

pk=require "openssl.pkey"
a=pk.new()
k=a:getPrivateKey("aes-256-cbc", "bar")
b = pk.new(k, "PEM", "private", "bar")

But the following invocations fail (with mostly hard to understand/debug error messages)

Passing "public" rather than "private":

$ lua -e 'pk=require "openssl.pkey";  a=pk.new(); k=a:getPrivateKey("aes-256-cbc", "bar") pk.new(k, "PEM", "public", "bar")'
lua: pkey.new: pem_lib.c:691:error:0906D06C:PEM routines:PEM_read_bio:no start line
stack traceback:
	[C]: in function 'openssl.pkey.new'
	(command line):1: in main chunk
	[C]: in ?

Passing wrong password (this is probably fine):

$ lua -e 'pk=require "openssl.pkey";  a=pk.new(); k=a:getPrivateKey("aes-256-cbc", "bar") pk.new(k, "PEM", "private", "foo")'
lua: pkey.new: evp_enc.c:536:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
stack traceback:
	[C]: in function 'openssl.pkey.new'
	(command line):1: in main chunk
	[C]: in ?

Passing wrong password and also no public vs private:

$ lua -e 'pk=require "openssl.pkey";  a=pk.new(); k=a:getPrivateKey("aes-256-cbc", "bar") pk.new(k, "PEM", nil, "foo")'
lua: pkey.new: pem_lib.c:691:error:0906D06C:PEM routines:PEM_read_bio:no start line
stack traceback:
	[C]: in function 'openssl.pkey.new'
	(command line):1: in main chunk
	[C]: in ?

Passing wrong password and no "PEM" choice:

$ lua -e 'pk=require "openssl.pkey";  a=pk.new(); k=a:getPrivateKey("aes-256-cbc", "bar") pk.new(k, nil, "private", "foo")'
lua: pkey.new: tasn_dec.c:1129:error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
stack traceback:
	[C]: in function 'openssl.pkey.new'
	(command line):1: in main chunk
	[C]: in ?

Otherwise, I found it troublesome that not passing a cipher would result in no password being used.
Perhaps throw an error if a password is specified and a cipher isn't?

@kunkku
Copy link
Contributor Author

kunkku commented Sep 2, 2018

I was thinking if the API could be something like this:

pk = require "openssl.pkey"
a = pk.new()
k = a:toPEM{type="private", cipher="aes-256-cbc", password="bar"}
b = pk.new(k, {format="PEM", type="private", password="bar"})

This would make the code more readable and there would be no need for a separate getPrivateKey method.

@kunkku
Copy link
Contributor Author

kunkku commented Sep 29, 2018

The new patch set implements passing options via a table as suggested above, avoiding the need for a new getPrivateKey method.

@vinayakhulawale
Copy link

@kunkku - Is this patch safe to use ? are there any further changes expected ?

@kunkku
Copy link
Contributor Author

kunkku commented Dec 27, 2019

Honestly, I do not know. I would like to get feedback from the maintainers.

src/openssl.c Outdated
char *pass = (char *) u;
strncpy(buf, pass, size);
return MIN(strlen(pass), (unsigned int) size);
} /* pem_password_cb() */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs fixing

src/openssl.c Outdated
lua_State *L = (lua_State *) u;

if (lua_isnil(L, -1) || (lua_isfunction(L, -1) && lua_pcall(L, 0, 1, 0)))
return 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't keep the stack balanced: if the field is nil then nothing is pushed. If the pcall results in an error then the error will be left on the stack.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this should work?

diff --git a/src/openssl.c b/src/openssl.c
index 35183ab..868a6d3 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -4073,9 +4073,14 @@ static BIO *getbio(lua_State *L) {
 static int pem_pw_cb(char *buf, int size, int rwflag, void *u) {
        lua_State *L = (lua_State *) u;
 
-       if (lua_isnil(L, -1) || (lua_isfunction(L, -1) && lua_pcall(L, 0, 1, 0)))
+       if (lua_isnil(L, -1))
                return 0;
 
+       if (lua_isfunction(L, -1) && lua_pcall(L, 0, 1, 0)) {
+               lua_pop(L, 1);
+               return 0;
+       }
+
        const char *pass = lua_tostring(L, -1);
        if (!pass)
                return 0;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the current implementation. In case of error, the error is replaced by nil to make the callback return directly if called again. The callback does not change the size of the stack on any execution path.

algitbot pushed a commit to alpinelinux/aports that referenced this pull request Jan 12, 2020
lua_getfield(L, 2, "type");
lua_getfield(L, 2, "password");
} else
lua_pushnil(L);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch only pushes one item vs the other branch's 3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the net amount of pushed items is 1 in both branches.

@@ -4062,10 +4070,31 @@ static BIO *getbio(lua_State *L) {
} /* getbio() */


static int pem_pw_cb(char *buf, int size, int rwflag, void *u) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth adding a comment of "expects nil/function/string on top of the stack" and "leaves one item on the top of the stack".

"errors from the function are not reported"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps it should leave nothing on the stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested comments added.

return luaL_error(L, "pkey:toPEM: password not defined");
}

if (!PEM_write_bio_PrivateKey(bio, key, cipher, NULL, 0, pem_pw_cb, L))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there is no cname on the stack then what will be at top of stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an else branch which pushes a nil value. The value will not be used because openssl will not invoke the callback when cipher equals NULL.

@kunkku kunkku force-pushed the password branch 2 times, most recently from dd01937 to 005b8bf Compare June 19, 2020 11:10
size_t len;
BIO *bio;
EVP_PKEY *pub = NULL, *prvt = NULL;
int goterr = 0;

if (lua_istable(L, 2)) {
lua_pop(L, 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be popping the table itself; or the 3rd argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always the 3rd argument because stack size is set to 3 on line 4101. Of course, this can be changed to lua_remove if you prefer it that way.


/* #1 key, #2 format, #3 type, #4 password or callback */

data = luaL_checklstring(L, 1, &len);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the error message for this will have the wrong index

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should it be then? This was just moved from line 4321 to process the arguments in ascending order. The purpose was to improve readability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants