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

HMAC signing for http push #5

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

HMAC signing for http push #5

wants to merge 10 commits into from

Conversation

redmoo
Copy link
Contributor

@redmoo redmoo commented Jul 20, 2017

This is part of my contribution to Google Summer of Code 2017.

I implemented HMAC signing for http push, but I'm not quite sure about a few things.

Was making a custom header a correct approach (like in the python hmac example script)?
Is using OpenSSL ok to calculate HMAC? Is it available on target devices?

I'm sure there's more.

edit: oops.. seems it added the already merged readme by mistake.

@robimarko
Copy link
Contributor

Currently there is only python-openssl package preinstalled in images that are generated by nodewatcher

@mitar
Copy link
Member

mitar commented Jul 20, 2017

Please use a more descriptive branch name and not development. I will rename it for you now.

@mitar
Copy link
Member

mitar commented Jul 20, 2017

OK, one cannot change source branches. So in the future change this. For now we can leave it like this.

@mitar
Copy link
Member

mitar commented Jul 20, 2017

Please rebase it on the latest master. You have some commits from before in this branch. For every PR you should create a new clean branch. And every PR should be a rounded unit of contribution. Like in the past you made README improvement. Now it hmac support.

@mitar
Copy link
Member

mitar commented Jul 20, 2017

Currently there is only python-openssl package preinstalled in images that are generated by nodewatcher

Why would a python package be installed on nodes? Interesting.

@mitar
Copy link
Member

mitar commented Jul 20, 2017

And no. Do not close the pull request and redo it again. Learn how to rebase an existing branch and do it here and update this branch and pull request.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 20, 2017

Oh.. my mistake. Just pushed my local branch. Will have separate descriptive ones from now on.

Hmm, so this means I have to use something other than OpenSSL... I guess installing OpenSSL is not an option?

Will look into rebasing.

@mitar
Copy link
Member

mitar commented Jul 21, 2017

Hmm, so this means I have to use something other than OpenSSL... I guess installing OpenSSL is not an option?

No, you should check what packages are available and what libraries are available.

@robimarko
Copy link
Contributor

@mitar Well it is not preinstalled but rather it is built and available in our image builders.
You can see it on package list in firmware-core

Sent from my Xiaomi Redmi Note 3 using FastHub

@mitar
Copy link
Member

mitar commented Jul 21, 2017

Yes, but that is different. What we would need here is to know which crypto library is already installed. We have TLS, so which library is used for it. HMAC should use the same one.

BTW, HMAC is very simple. Only a hash function is needed. So maybe we do not need whole crypto library just to have a hash function. So, we should see if we have a hash function anywhere already available.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 21, 2017

Libcurl seems to be linked with other libraries to supplement its need for SSL support etc. It's using "gnutls" but I don't think it's available to freely use in the code. Any ideas?

@kostko
Copy link
Member

kostko commented Jul 21, 2017

BTW, HMAC is very simple. Only a hash function is needed. So maybe we do not need whole crypto library just to have a hash function. So, we should see if we have a hash function anywhere already available.

Exactly, so the whole point of HMAC-based authentication is that it is much simpler than TLS. Because if you have TLS then you can use TLS, but the TLS libraries are quite big. It would be much better if you could either find a small library that implements HMAC-SHA256 or pull in a small HMAC-SHA256 implementation.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 21, 2017

Oh.. That makes sense. Will look around.

#if LIBCURL_VERSION_NUM >= 0x072700
/* Pin server-side public key when configured. */
char *server_pubkey = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_server_pubkey");
char *auth_type = nw_uci_get_string(uci, "nodewatcher.@agent[0].authentication_method");
Copy link
Member

Choose a reason for hiding this comment

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

I would rename this to push_authentication_method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

@@ -49,6 +50,7 @@ static int nw_http_push_start_acquire_data(struct nodewatcher_module *module,

/* Get the configured URL from UCI. */
char *url = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_url");

Copy link
Member

Choose a reason for hiding this comment

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

Why this empty line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Readability habit I guess... You want me to stop doing that or just wanted an explanation? :)

if (server_pubkey) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, server_pubkey);
if (strcmp(auth_type, "hmac") == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Why is this inslide if (server_pubkey) block? It should be possible to configure no public key, only an HMAC (secret) key.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ohhh... My bad. Will add another option and separate block.

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, server_pubkey);
if (strcmp(auth_type, "hmac") == 0) {
unsigned char *hmac_result = HMAC(EVP_sha256(), server_pubkey, strlen(server_pubkey), (unsigned char *)data_string, strlen(data_string), NULL, NULL);
Copy link
Member

Choose a reason for hiding this comment

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

I see, so you are using server_pubkey as the secret key for HMAC. This is not good, you should instead introduce a new configuration option for this new key.

@@ -22,6 +22,7 @@

#include <syslog.h>
#include <curl.h>
#include <openssl/hmac.h>
Copy link
Member

Choose a reason for hiding this comment

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

Is there some smaller HMAC implementation available? We don't want to depend on OpenSSL.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, will look around if there's something appropriate and ask here if that's ok.

@kostko
Copy link
Member

kostko commented Jul 21, 2017

Currently there is only python-openssl package preinstalled in images that are generated by nodewatcher
Why would a python package be installed on nodes? Interesting.

There are no Python packages installed by default? Only in case you generate firmware for the tunneldigger broker, which is written in Python.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 21, 2017

Is something like this still too much and should I look for just an sha256 implementation?

@mitar
Copy link
Member

mitar commented Jul 21, 2017

You only need one hashing function, like SHA-256. Not the whole library of those.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 23, 2017

So.. What is the protocol with adding convenience functions to other people's code? Depending on licence? Because it would be really unfortunate if I legally couldn't change bad macro definitions or have to add related methods to other files...

@mitar
Copy link
Member

mitar commented Jul 23, 2017

So.. What is the protocol with adding convenience functions to other people's code? Depending on licence? Because it would be really unfortunate if I legally couldn't change bad macro definitions or have to add related methods to other files...

What do you mean? Which code?

@redmoo
Copy link
Contributor Author

redmoo commented Jul 23, 2017

sha256 implementation from someone else. I will find another one anyway but it would be good to know if I can dabble in it.

@mitar
Copy link
Member

mitar commented Jul 23, 2017

I will find another one anyway but it would be good to know if I can dabble in it.

I mean, as you said. Depending on the license of that code. But probably if the license does not allow you to change the code, the we do not want that code in our code anyway.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 23, 2017

Hah.. Good point. Ok. I'll push it all anyway so people can comment... The sha256 implementation will be changed though.

@robimarko
Copy link
Contributor

As far as I can see you are using this library?
https://github.com/B-Con/crypto-algorithms

If that is the case then the author has listed this as licence info:
This code is released into the public domain free of any restrictions. The author requests acknowledgement if the code is used, but does not require it. This code is provided free of any liability and without any quality claims by the author.

But the author himself has stated this:
Note that these are not cryptographically secure implementations. They have no resistence to side-channel attacks and should not be used in contexts that need cryptographically secure implementations.

These algorithms are not optimized for speed or space. They are primarily designed to be easy to read, although some basic optimization techniques have been employed

Therefor I dont think these are really good for use

@redmoo
Copy link
Contributor Author

redmoo commented Jul 23, 2017

No it is not a good option, but I commited it anyway so you can all comment about the hmac portion or http_push part. I found another proper solution but it's way too fragmented and didn't even work correctly (failed the tests), so I'm still looking for a good option to replace this sha implementation.

@robimarko
Copy link
Contributor

I understand.

@redmoo
Copy link
Contributor Author

redmoo commented Jul 24, 2017

So... After some reading and one unsuccessful porting I have a dilemma. The current implementation is supposed to be vulnerable to side channel attacks. Ok. But so is openssl and many other libraries, especially when using the default C implementation (plus you need physical access to the device). It doesn't seem like it's as easy as porting a bit of code from some library to fix it. It depends on other libraries for memory access etc. Really time consuming if we are to get any benefit from it.

I propose that we use this for now as it does work, pardon the phrase, "good enough" and I focus on my remaining duties. This can still be used and upgraded at a later date. Which I can do, after other assignments are done.

common/hmac.c Outdated
sha256_final(&ctx, buf);
}

void hmac(BYTE *key, int key_len, const BYTE *data, int data_len, BYTE *hmac_out)
Copy link
Member

Choose a reason for hiding this comment

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

This is HMAC-SHA256, name accoordingly (e.g. hmac_sha256).

sha256(in_buf, hmac_out);

free(in_buf);
}
Copy link
Member

Choose a reason for hiding this comment

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

Add newline at end of file.

Copy link
Member

Choose a reason for hiding this comment

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

Also elsewhere.

Copy link
Member

Choose a reason for hiding this comment

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

sha256() wrapper only works correctly for strings.

Why the XORs? Oh, I see, because sha256() doesn't work correctly when it encounters \0.

Assembling buffer copies into newly allocated buffers should not be needed - just use the primitives from sha256()

key_len == SHA256_BLOCK_SIZE case not handled.

Finally, why reinventing the wheel?

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_to_json_string(data));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_string);

const char *auth_type = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_authentication_method"); // TODO: is hmac default?
Copy link
Member

Choose a reason for hiding this comment

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

No, it should be backwards compatible so default should be as before (public key).


if (hmac_key) {
unsigned char hmac_out[SHA256_HASH_SIZE];
hmac((unsigned char *)hmac_key, strlen(hmac_key), (unsigned char *)data_string, strlen(data_string), hmac_out);
Copy link
Member

Choose a reason for hiding this comment

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

No space before pointer, space after cast, e.g. (unsigned char*) hmac_key. Also elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. Just to nitpick... Elsewhere it's always "char *var", so it made sense to me to keep it the same way. :)

@redmoo
Copy link
Contributor Author

redmoo commented Jul 24, 2017

Before merging, what about licence? I didn't paste anything to my new files... Should I have? I guess the same as in the other files with updated name etc..

Copy link
Member

@domenpk domenpk left a comment

Choose a reason for hiding this comment

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

hmac appears to be broken, since it's limited to C string input.

  • other small comments

sha256(in_buf, hmac_out);

free(in_buf);
}
Copy link
Member

Choose a reason for hiding this comment

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

sha256() wrapper only works correctly for strings.

Why the XORs? Oh, I see, because sha256() doesn't work correctly when it encounters \0.

Assembling buffer copies into newly allocated buffers should not be needed - just use the primitives from sha256()

key_len == SHA256_BLOCK_SIZE case not handled.

Finally, why reinventing the wheel?


char signature[SHA256_HASH_SIZE + 1] = {0};

if (nw_base64_encode(hmac_out, SHA256_HASH_SIZE, signature, SHA256_HASH_SIZE) == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

base64 inflates the output size, so this probably isn't correct.


if (nw_base64_encode(hmac_out, SHA256_HASH_SIZE, signature, SHA256_HASH_SIZE) == 0) {
char signature_header[26+sizeof(signature)] = {0};
strcat(strcpy(signature_header, "X-Nodewatcher-Signature: "), signature);
Copy link
Member

Choose a reason for hiding this comment

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

Odd at least. Can you use snprintf?
Or, how about just initializing to "X-Nodewatcher-Signature: " instead of {0}?

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

Successfully merging this pull request may close these issues.

5 participants