Skip to content
New issue

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

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

Already on GitHub? # to your account

Add support for getting/setting GCM authentication tag (fixes #115) #201

Closed
wants to merge 6 commits into from

Conversation

mwild1
Copy link
Contributor

@mwild1 mwild1 commented Jun 29, 2022

See #115 (comment) for context.


luaL_buffinit(L, &tag);

if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_size, (void*)luaL_prepbuffsize(&tag, tag_size))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

When was EVP_CIPHER_CTX_ctrl introduced? luaossl still tries to be compatible with old openssl versions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe it's been around a "long time". I don't know if it was in 0.9.8 (I can dig if really necessary), but it's certainly in 1.0.2. It's due to be retired post-3.0.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's due to be retired post-3.0.

What is it going to be replaced with?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A new OSSL_PARAM API they've introduced. I haven't looked too much into it so far, as I haven't been working on any code that can be 3.0.x-only just yet.

@@ -11975,6 +11975,47 @@ static int cipher_final(lua_State *L) {
} /* cipher_final() */


static int cipher_get_tag(lua_State *L) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be cipher_gcm_get_tag or similar to follow the naming of the option?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly. It's actually not unique to GCM, but to the family of AEAD modes - in OpenSSL this includes CCM. In 1.1.x there is now therefore an alias for EVP_CTRL_AEAD_GET_TAG but this constant is not defined in 1.0.x. The current code should work with both GCM and CCM.

If we change the name, we could call it e.g. :get_aead_tag(), but I don't know whether this level of disambiguation is necessary.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it be sensible to use EVP_CIPHER_type to get the type of the cipher and then use the relevant constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so. As I mentioned, modern versions of OpenSSL have both CCM_GET_TAG and GCM_GET_TAG aliased to AEAD_GET_TAG. The latter was added in 1.1, but before this the CCM variant was an alias of GCM_GET_TAG anyway.

In summary: in old versions, GCM_GET_TAG and CCM_GET_TAG are interchangeable identifiers for the same operation. In newer versions (1.1+) AEAD_GET_TAG was added as a generic identifier for this operation.

References:

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add a comment saying as much?

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 in 4a8b770

@mwild1
Copy link
Contributor Author

mwild1 commented Jun 30, 2022

The following code snippet can be used to test GCM encryption (it's based on the code originally posted as broken in #115):

local cipher = require "openssl.cipher"
local key = "abcdefghijklmnopabcdefghijklmnop"
local iv = "123456123456"
local message = "My secret message"

local c = cipher.new("aes-256-gcm"):encrypt(key, iv);
local encrypted = c:update(message)
assert(c:final());
local tag = assert(c:getTag(16));

print("Encrypted to "..#encrypted.." bytes with "..#tag.."-byte tag");

-- Now for the decryption
local aes = cipher.new("aes-256-gcm"):decrypt(key, iv)
-- Provide the expected authentication tag to OpenSSL
aes:setTag(tag);
local decrypted = aes:update(encrypted)
print(decrypted)
print(aes:final())

CCM can be tested by changing c:getTag(16) to c:getTag(12) and obviously changing -gcm to -ccm in the cipher name.

@daurnimator
Copy link
Collaborator

The following code snippet ...

Could you add this as a regression test?

@mwild1
Copy link
Contributor Author

mwild1 commented Jun 30, 2022

Regression test added in e402818

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.

Couple of nits; otherwise LGTM.

src/openssl.c Outdated
Comment on lines 12040 to 12041
{ "getTag", &cipher_get_tag },
{ "setTag", &cipher_set_tag },
Copy link
Collaborator

Choose a reason for hiding this comment

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

These need whitespace for alignment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0dad2b3

src/openssl.c Outdated
auxL_pusherror(L, auxL_EOPENSSL, NULL);

return 2;
} /* cipher_get_tag() */
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
} /* cipher_get_tag() */
} /* cipher_set_tag() */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0dad2b3

@daurnimator
Copy link
Collaborator

Merged with bed73b6

@daurnimator daurnimator closed this Jul 3, 2022
@daurnimator
Copy link
Collaborator

@mwild1 I'm only running your regression tests now, and I get:

$ lua 115-test-aead.lua 
lua: 115-test-aead.lua:12: bad argument #2 to 'encrypt' (12: invalid IV length (should be 7))
stack traceback:
	[C]: in method 'encrypt'
	115-test-aead.lua:12: in function 'test_aead'
	115-test-aead.lua:36: in main chunk
	[C]: in ?

@mwild1
Copy link
Contributor Author

mwild1 commented Jul 7, 2022

Well that's interesting. The test passes fine for me as-is. If I change the IV length to 7, I get:

lua: 115-test-aead.lua:12: bad argument #2 to 'encrypt' (7: invalid IV length (should be 12))
stack traceback:
	[C]: in function 'encrypt'
	115-test-aead.lua:12: in function 'test_aead'
	115-test-aead.lua:37: in main chunk
	[C]: in ?

This is with OpenSSL 1.1.1.

Although I can't find it now, I vaguely recall some documentation that indicated the iv_length() function was not reliable for ciphers that accept IVs of variable/multiple lengths.

My guess is that maybe the default IV length has changed between versions. Out of curiosity, what version was your result with?

Some further reading:

So in 1.1.1+ if we explicitly set the IV length (which might be sensible anyway if the default varies between versions) then this issue should go away. But if we want backwards-compatibility with pre-1.1.1, I guess we need to skip this check for these ciphers.

@daurnimator
Copy link
Collaborator

If I change the IV length to 7, I get:

lua: 115-test-aead.lua:12: bad argument #2 to 'encrypt' (7: invalid IV length (should be 12))
stack traceback:
	[C]: in function 'encrypt'
	115-test-aead.lua:12: in function 'test_aead'
	115-test-aead.lua:37: in main chunk
	[C]: in ?

Me too:

$ git diff
diff --git a/regress/115-test-aead.lua b/regress/115-test-aead.lua
index ed0f7f8..5026ce1 100644
--- a/regress/115-test-aead.lua
+++ b/regress/115-test-aead.lua
@@ -5,7 +5,7 @@ local cipher = require "openssl.cipher"
 
 -- Test AES-256-GCM
 local key = "abcdefghijklmnopabcdefghijklmnop"
-local iv = "123456123456"
+local iv = "1234561"
 local message = "My secret message"
 
 function test_aead(params)
$ lua 115-test-aead.lua 
lua: 115-test-aead.lua:12: bad argument #2 to 'encrypt' (7: invalid IV length (should be 12))
stack traceback:
	[C]: in method 'encrypt'
	115-test-aead.lua:12: in function 'test_aead'
	115-test-aead.lua:31: in main chunk
	[C]: in ?

Out of curiosity, what version was your result with?

$ openssl version
OpenSSL 1.1.1p  21 Jun 2022

So in 1.1.1+ if we explicitly set the IV length (which might be sensible anyway if the default varies between versions) then this issue should go away.

I'm running new enough that the fix you mentioned should be in. I guess it's something else...

@daurnimator
Copy link
Collaborator

daurnimator commented Jul 7, 2022

oh; I only just noticed the line number in the stack trace: the error moves from 36 to 31 when the IV changes length.

So I guess for aes-256-gcm it wants 12; but for aes-256-ccm it wants 7? or some variable length where it's currently initialised to 7?

@mwild1
Copy link
Contributor Author

mwild1 commented Jul 7, 2022

Yeah, I made these changes for testing:

diff --git a/regress/115-test-aead.lua b/regress/115-test-aead.lua
index ed0f7f8..febea2d 100644
--- a/regress/115-test-aead.lua
+++ b/regress/115-test-aead.lua
@@ -9,7 +9,7 @@ local iv = "123456123456"
 local message = "My secret message"
 
 function test_aead(params)
-       local c = cipher.new(params.cipher):encrypt(key, iv)
+       local c = cipher.new(params.cipher):encrypt(key, params.iv)
 
        local encrypted = c:update(message)
        regress.check(encrypted)
@@ -31,9 +31,11 @@ end
 test_aead {
        cipher = "aes-256-gcm";
        tag_length = 16;
+       iv = iv;
 }
 
 test_aead {
        cipher = "aes-256-ccm";
        tag_length = 12;
+       iv = iv:sub(1, 7);
 }

@daurnimator
Copy link
Collaborator

@mwild1 how about #202 ?

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

Successfully merging this pull request may close these issues.

2 participants