-
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?
Changes from all commits
9d0572d
0009463
a44c43e
8304689
8f92420
da088ef
9489e58
8453843
7abca81
7e13185
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* nodewatcher-agent - remote monitoring daemon | ||
* | ||
* Copyright (C) 2017 Anej Placer <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify it | ||
* under the terms of the GNU Affero General Public License as published by the | ||
* Free Software Foundation, either version 3 of the License, or (at your | ||
* option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License | ||
* for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#include <nodewatcher-agent/hmac.h> | ||
|
||
void sha256(const BYTE *text, BYTE *buf) | ||
{ | ||
SHA256_CTX ctx; | ||
sha256_init(&ctx); | ||
sha256_update(&ctx, text, strlen((char*) text)); | ||
sha256_final(&ctx, buf); | ||
} | ||
|
||
void hmac_sha256(BYTE *key, int key_len, const BYTE *data, int data_len, BYTE *hmac_out) | ||
{ | ||
BYTE key_buf[SHA256_BLOCK_SIZE] = {0}; | ||
|
||
if (key_len > SHA256_BLOCK_SIZE) { | ||
sha256(key, key_buf); | ||
} | ||
if (key_len < SHA256_BLOCK_SIZE) { | ||
memcpy(key_buf, key, key_len); | ||
} | ||
|
||
for (int i = 0; i < SHA256_BLOCK_SIZE; i++) { | ||
key_buf[i] ^= 0x36; | ||
} | ||
|
||
size_t buf_size = SHA256_BLOCK_SIZE + data_len; | ||
BYTE *in_buf = (BYTE*) malloc(buf_size); | ||
memset(in_buf, 0x00, buf_size); | ||
memcpy(in_buf, key_buf, SHA256_BLOCK_SIZE); | ||
memcpy(in_buf + SHA256_BLOCK_SIZE, data, data_len); | ||
memset(hmac_out, 0x00, SHA256_HASH_SIZE); | ||
sha256(in_buf, hmac_out); | ||
|
||
for (int i = 0; i < SHA256_BLOCK_SIZE; i++) { | ||
key_buf[i] ^= 0x36 ^ 0x5c; | ||
} | ||
|
||
buf_size = SHA256_BLOCK_SIZE + SHA256_HASH_SIZE + 1; | ||
in_buf = (BYTE*) realloc(in_buf, buf_size); | ||
memset(in_buf, 0x00, buf_size); | ||
memcpy(in_buf, key_buf, SHA256_BLOCK_SIZE); | ||
memcpy(in_buf + SHA256_BLOCK_SIZE, hmac_out, SHA256_HASH_SIZE); | ||
memset(hmac_out, 0x00, SHA256_HASH_SIZE); | ||
sha256(in_buf, hmac_out); | ||
|
||
free(in_buf); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
/********************************************************************* | ||
* Filename: sha256.c | ||
* Author: Brad Conte (brad AT bradconte.com) | ||
* Copyright: | ||
* Disclaimer: This code is presented "as is" without any guarantees. | ||
* Details: Implementation of the SHA-256 hashing algorithm. | ||
SHA-256 is one of the three algorithms in the SHA2 | ||
specification. The others, SHA-384 and SHA-512, are not | ||
offered in this implementation. | ||
Algorithm specification can be found here: | ||
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf | ||
This implementation uses little endian byte order. | ||
*********************************************************************/ | ||
|
||
/*************************** HEADER FILES ***************************/ | ||
#include <nodewatcher-agent/sha256.h> | ||
|
||
#include <stdlib.h> | ||
#include <memory.h> | ||
|
||
/****************************** MACROS ******************************/ | ||
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) | ||
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) | ||
|
||
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) | ||
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) | ||
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) | ||
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) | ||
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) | ||
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) | ||
|
||
/**************************** VARIABLES *****************************/ | ||
static const WORD k[64] = { | ||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, | ||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, | ||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, | ||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, | ||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, | ||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, | ||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, | ||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 | ||
}; | ||
|
||
/*********************** FUNCTION DEFINITIONS ***********************/ | ||
void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) | ||
{ | ||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; | ||
|
||
for (i = 0, j = 0; i < 16; ++i, j += 4) | ||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); | ||
for ( ; i < 64; ++i) | ||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; | ||
|
||
a = ctx->state[0]; | ||
b = ctx->state[1]; | ||
c = ctx->state[2]; | ||
d = ctx->state[3]; | ||
e = ctx->state[4]; | ||
f = ctx->state[5]; | ||
g = ctx->state[6]; | ||
h = ctx->state[7]; | ||
|
||
for (i = 0; i < 64; ++i) { | ||
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; | ||
t2 = EP0(a) + MAJ(a,b,c); | ||
h = g; | ||
g = f; | ||
f = e; | ||
e = d + t1; | ||
d = c; | ||
c = b; | ||
b = a; | ||
a = t1 + t2; | ||
} | ||
|
||
ctx->state[0] += a; | ||
ctx->state[1] += b; | ||
ctx->state[2] += c; | ||
ctx->state[3] += d; | ||
ctx->state[4] += e; | ||
ctx->state[5] += f; | ||
ctx->state[6] += g; | ||
ctx->state[7] += h; | ||
} | ||
|
||
void sha256_init(SHA256_CTX *ctx) | ||
{ | ||
ctx->datalen = 0; | ||
ctx->bitlen = 0; | ||
ctx->state[0] = 0x6a09e667; | ||
ctx->state[1] = 0xbb67ae85; | ||
ctx->state[2] = 0x3c6ef372; | ||
ctx->state[3] = 0xa54ff53a; | ||
ctx->state[4] = 0x510e527f; | ||
ctx->state[5] = 0x9b05688c; | ||
ctx->state[6] = 0x1f83d9ab; | ||
ctx->state[7] = 0x5be0cd19; | ||
} | ||
|
||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) | ||
{ | ||
WORD i; | ||
|
||
for (i = 0; i < len; ++i) { | ||
ctx->data[ctx->datalen] = data[i]; | ||
ctx->datalen++; | ||
if (ctx->datalen == 64) { | ||
sha256_transform(ctx, ctx->data); | ||
ctx->bitlen += 512; | ||
ctx->datalen = 0; | ||
} | ||
} | ||
} | ||
|
||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]) | ||
{ | ||
WORD i; | ||
|
||
i = ctx->datalen; | ||
|
||
// Pad whatever data is left in the buffer. | ||
if (ctx->datalen < 56) { | ||
ctx->data[i++] = 0x80; | ||
while (i < 56) | ||
ctx->data[i++] = 0x00; | ||
} | ||
else { | ||
ctx->data[i++] = 0x80; | ||
while (i < 64) | ||
ctx->data[i++] = 0x00; | ||
sha256_transform(ctx, ctx->data); | ||
memset(ctx->data, 0, 56); | ||
} | ||
|
||
// Append to the padding the total message's length in bits and transform. | ||
ctx->bitlen += ctx->datalen * 8; | ||
ctx->data[63] = ctx->bitlen; | ||
ctx->data[62] = ctx->bitlen >> 8; | ||
ctx->data[61] = ctx->bitlen >> 16; | ||
ctx->data[60] = ctx->bitlen >> 24; | ||
ctx->data[59] = ctx->bitlen >> 32; | ||
ctx->data[58] = ctx->bitlen >> 40; | ||
ctx->data[57] = ctx->bitlen >> 48; | ||
ctx->data[56] = ctx->bitlen >> 56; | ||
sha256_transform(ctx, ctx->data); | ||
|
||
// Since this implementation uses little endian byte ordering and SHA uses big endian, | ||
// reverse all the bytes when copying the final state to the output hash. | ||
for (i = 0; i < 4; ++i) { | ||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; | ||
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* nodewatcher-agent - remote monitoring daemon | ||
* | ||
* Copyright (C) 2017 Anej Placer <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify it | ||
* under the terms of the GNU Affero General Public License as published by the | ||
* Free Software Foundation, either version 3 of the License, or (at your | ||
* option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License | ||
* for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#ifndef NODEWATCHER_AGENT_HMAC_H | ||
#define NODEWATCHER_AGENT_HMAC_H | ||
|
||
#include <nodewatcher-agent/sha256.h> | ||
|
||
#define SHA256_HASH_SIZE 32 | ||
#define SHA256_BLOCK_SIZE 64 | ||
|
||
void sha256(const BYTE *text, BYTE *buf); | ||
void hmac_sha256(BYTE *key, int key_len, const BYTE *data, int data_len, BYTE *hmac_out); | ||
|
||
#endif // NODEWATCHER_AGENT_HMAC_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/********************************************************************* | ||
* Filename: sha256.h | ||
* Author: Brad Conte (brad AT bradconte.com) | ||
* Copyright: | ||
* Disclaimer: This code is presented "as is" without any guarantees. | ||
* Details: Defines the API for the corresponding SHA1 implementation. | ||
*********************************************************************/ | ||
|
||
#ifndef SHA256_H | ||
#define SHA256_H | ||
|
||
/*************************** HEADER FILES ***************************/ | ||
#include <stddef.h> | ||
|
||
/**************************** DATA TYPES ****************************/ | ||
typedef unsigned char BYTE; // 8-bit byte | ||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines | ||
|
||
typedef struct { | ||
BYTE data[64]; | ||
WORD datalen; | ||
unsigned long long bitlen; | ||
WORD state[8]; | ||
} SHA256_CTX; | ||
|
||
/*********************** FUNCTION DECLARATIONS **********************/ | ||
void sha256_init(SHA256_CTX *ctx); | ||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); | ||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]); | ||
|
||
#endif // SHA256_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,9 +19,11 @@ | |
#include <nodewatcher-agent/module.h> | ||
#include <nodewatcher-agent/json.h> | ||
#include <nodewatcher-agent/utils.h> | ||
#include <nodewatcher-agent/hmac.h> | ||
|
||
#include <syslog.h> | ||
#include <curl.h> | ||
#include <strings.h> | ||
|
||
/* Timestamp when last successful push occurred. */ | ||
static time_t last_push_at = 0; | ||
|
@@ -49,6 +51,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"); | ||
|
||
if (url) { | ||
/* Collect all the module data and perform the push. */ | ||
json_object *data = nw_module_get_output(); | ||
|
@@ -61,30 +64,66 @@ static int nw_http_push_start_acquire_data(struct nodewatcher_module *module, | |
/* Default. */ | ||
timeout = 5; | ||
} | ||
|
||
const char* data_string = json_object_to_json_string(data); | ||
|
||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); | ||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); | ||
curl_easy_setopt(curl, CURLOPT_URL, url); | ||
curl_easy_setopt(curl, CURLOPT_POST, 1); | ||
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"); | ||
|
||
if (strcasecmp(auth_type, "hmac") == 0) { | ||
const char *hmac_key = nw_uci_get_string(uci, "nodewatcher.@agent[0].hmac_key"); | ||
|
||
if (hmac_key) { | ||
unsigned char hmac_out[SHA256_HASH_SIZE]; | ||
hmac_sha256((unsigned char*) hmac_key, strlen(hmac_key), (unsigned char*) data_string, strlen(data_string), hmac_out); | ||
|
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. base64 inflates the output size, so this probably isn't correct. |
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Odd at least. Can you use snprintf? |
||
|
||
struct curl_slist *chunk = NULL; | ||
chunk = curl_slist_append(chunk, "X-Nodewatcher-Signature-Algorithm: hmac-sha256"); | ||
chunk = curl_slist_append(chunk, signature_header); | ||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); | ||
} | ||
} | ||
|
||
free((char*) hmac_key); | ||
|
||
} else { | ||
|
||
#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"); | ||
if (server_pubkey) { | ||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); | ||
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, server_pubkey); | ||
free(server_pubkey); | ||
} | ||
/* Pin server-side public key when configured. */ | ||
char *server_pubkey = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_server_pubkey"); | ||
|
||
if (server_pubkey) { | ||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); | ||
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, server_pubkey); | ||
free(server_pubkey); | ||
} | ||
#endif | ||
/* Setup client authentication when configured. */ | ||
char *client_certificate = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_client_certificate"); | ||
char *client_key = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_client_key"); | ||
if (client_certificate && client_key) { | ||
curl_easy_setopt(curl, CURLOPT_SSLCERT, client_certificate); | ||
curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key); | ||
/* Setup client authentication when configured. */ | ||
char *client_certificate = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_client_certificate"); | ||
char *client_key = nw_uci_get_string(uci, "nodewatcher.@agent[0].push_client_key"); | ||
|
||
if (client_certificate && client_key) { | ||
curl_easy_setopt(curl, CURLOPT_SSLCERT, client_certificate); | ||
curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key); | ||
} | ||
|
||
free(client_certificate); | ||
free(client_key); | ||
} | ||
|
||
free(client_certificate); | ||
free(client_key); | ||
free((char*) data_string); | ||
free((char*) auth_type); | ||
|
||
/* Provide a buffer to store errors in. */ | ||
char errbuf[CURL_ERROR_SIZE]; | ||
|
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? :)