-
Notifications
You must be signed in to change notification settings - Fork 11
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
base: master
Are you sure you want to change the base?
Conversation
Currently there is only python-openssl package preinstalled in images that are generated by nodewatcher |
Please use a more descriptive branch name and not |
OK, one cannot change source branches. So in the future change this. For now we can leave it like this. |
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. |
Why would a python package be installed on nodes? Interesting. |
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. |
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. |
No, you should check what packages are available and what libraries are available. |
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. |
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? |
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. |
Oh.. That makes sense. Will look around. |
modules/http_push.c
Outdated
#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"); |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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"); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this empty line?
There was a problem hiding this comment.
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? :)
modules/http_push.c
Outdated
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
modules/http_push.c
Outdated
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); |
There was a problem hiding this comment.
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.
modules/http_push.c
Outdated
@@ -22,6 +22,7 @@ | |||
|
|||
#include <syslog.h> | |||
#include <curl.h> | |||
#include <openssl/hmac.h> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There are no Python packages installed by default? Only in case you generate firmware for the tunneldigger broker, which is written in Python. |
Is something like this still too much and should I look for just an sha256 implementation? |
You only need one hashing function, like SHA-256. Not the whole library of those. |
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? |
sha256 implementation from someone else. 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. |
Hah.. Good point. Ok. I'll push it all anyway so people can comment... The sha256 implementation will be changed though. |
As far as I can see you are using this library? If that is the case then the author has listed this as licence info: But the author himself has stated this: 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 |
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. |
I understand. |
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) |
There was a problem hiding this comment.
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); | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also elsewhere.
There was a problem hiding this comment.
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?
modules/http_push.c
Outdated
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? |
There was a problem hiding this comment.
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).
modules/http_push.c
Outdated
|
||
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
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.. |
There was a problem hiding this 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); | ||
} |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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}?
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.