Skip to content

Commit

Permalink
Simple C publisher and python client for verifying HMAC-SHA1 signed m…
Browse files Browse the repository at this point in the history
…essages with MQTT
  • Loading branch information
karlp committed Aug 15, 2011
0 parents commit 6a8e875
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Demo code snippets related to MQTT

msg_signing - C message publisher, signing messages with openssl
- python message consumer, verifying signatures


18 changes: 18 additions & 0 deletions msg_signing/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CC=gcc
CFLAGS= -Wall -I.
SOURCES=main.c uglylogging.c
OBJECTS=$(SOURCES:.c=.o)
EXECUTABLE=publisher
LDFLAGS=-lc -lcrypto -lmosquitto

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@

%.o : %.c
$(CC) -c $(CFLAGS) $< -o $@

clean:
rm -rf $(OBJECTS)
rm -rf $(EXECUTABLE)
59 changes: 59 additions & 0 deletions msg_signing/client_verifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'''
Example for verifying HMAC-SHA1 signed messages on MQTT topics.
By calculating the HMAC over the entire message body, we can be sure that the message has not been
tampered with, and was produced by someone who knows the shared secret.
Anyone can still read the message of course, and messages can be replayed at will and still
appear genuine. Timestamps and only accepting messages from the "recent" past can help with that
@author: karlp@remake.is
'''

import hashlib
import hmac
import logging
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(name)s - %(message)s")
log = logging.getLogger("main")

import mosquitto

def on_connect(rc):
log.info("Connected")

def on_message(msg):
"""
Looks for messages on topics like blahblah/signed/>hex_hmac_key_here<
"""
log.info("Received message on topic %s, length: %d bytes", msg.topic, msg.payloadlen)

if "/signed/" in msg.topic:
log.debug("message is signed! will attempt to verify, msg is: <%s>", msg.payload_str)
topic_remainder, sig = msg.topic.rsplit('/', 1)
log.debug("sig is: <%s>", sig)
key = "karl_loves_you"
rsig = hmac.new(key, msg.payload_str, hashlib.sha1).hexdigest()
if rsig == sig:
log.info("Signatures match, message appears genuine")
else:
log.warn("rsig != sig! message was tampered: %s != %s", rsig, sig)

else:
log.info("Message is: <%s>", msg.payload_str)

#create a client object
mqttc = mosquitto.Mosquitto("python_sub_karl_1234")

#define the callbacks
mqttc.on_message = on_message
mqttc.on_connect = on_connect

#connect
mqttc.connect("localhost", 1883, 60, True)

#subscribe to topic test
mqttc.subscribe("#", 2)

#keep connected to broker
while mqttc.loop() == 0:
pass
137 changes: 137 additions & 0 deletions msg_signing/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Basic demo of sending HMAC signed messages to an MQTT server
*
* The message HMAC is calculated, and put in the topic, like so
* /demo/signed/<hmac_signature_here>
*
* Karl Palsson <karlp@remake.is>
*
* Takes two optional arguments, the MQTT server host, and the shared key
* Default MQTT host is localhost
* Default shared key is "karl_loves_you"
*
* Released into the public domain as demonstration code
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>

#include <mosquitto.h>
#include <openssl/err.h>
#include <openssl/hmac.h>

#include "uglylogging.h"

#define LOG_TAG __FILE__
#define DLOG(format, args...) ugly_log(UDEBUG, LOG_TAG, format, ## args)
#define ILOG(format, args...) ugly_log(UINFO, LOG_TAG, format, ## args)
#define fatal(format, args...) ugly_log(UFATAL, LOG_TAG, format, ## args)

struct mosquitto* mosq;
char *key;
long int last_message_time;

int setup_mq(const char* host) {
int mq_maj, mq_min, mq_rev;
mosquitto_lib_version(&mq_maj, &mq_min, &mq_rev);
DLOG("You've got mosquitto version major:%d, minor:%d, rev:%d\n", mq_maj, mq_min, mq_rev);
mosquitto_lib_init();
pid_t pid = getpid();
char clientid[40];

snprintf(clientid, sizeof (clientid), "rme_signed_demo_%d", pid);
mosq = mosquitto_new(clientid, NULL);

ILOG("Connecting to %s\n", host);
mosquitto_connect(mosq, host, 1883, 15, true);
return 0;
}

/**
* Demonstrate sending a signed (HMAC) and then encrypted message to MQTT
* @param tt just something that varies between messages, so we can see it when decrypted
* @return
*/
int send_signed_message(time_t tt, char *shared_key) {

// Make message...
char data[200];
sprintf(data, "Signed Message with sequence: %ld", tt);

DLOG("pub SIGNED: %s\n", data);
unsigned char* hmac;
unsigned int result_len;

// printf "%s" "message to be signed..." | openssl sha1 -hmac "karl_hmac_key"
hmac = HMAC(EVP_sha1(), shared_key, strlen(shared_key), (unsigned char*)data, strlen(data), NULL, &result_len);
if (hmac == NULL) {
fatal("Couldn't sign the message: %s\n", ERR_error_string(ERR_get_error(), NULL));
}

// Add signature to message
char topic[60];
char *tmp = &topic[0];
tmp += sprintf(topic, "demo/signed/");
int i;
for (i = 0; i < result_len; i++) {
sprintf(tmp + (i*2), "%02x", hmac[i]);
}
i = mosquitto_publish(mosq, NULL, topic, strlen(data), (unsigned char*)data, 0, false);
if (i != MOSQ_ERR_SUCCESS) {
fatal("Failed to publish message: %d\n", i);
}
return 0;
}

int send_insecure_message(time_t tt) {
char msg[128];
sprintf(msg, "Insecure Message with sequence: %ld", tt);
mosquitto_publish(mosq, NULL, "demo/insecure", strlen(msg), (unsigned char*)msg, 0, false);
DLOG("pub unsigned: %s\n", msg);
return 0;
}

void run_tasks() {
mosquitto_loop(mosq, -1);
// anything else we like here....

time_t tt = time(NULL);
if ((long int) tt > last_message_time + 2) {
last_message_time = tt;
send_insecure_message(tt);
send_signed_message(tt, key);
}
sleep(1);
}

/*
* Do mad wild shit that makes us metric boatloads of cash money
*/
int main(int argc, char** argv) {
ugly_init(99);

if (argc < 2) {
DLOG("(pass a mq host as argument, otherwise we use localhost)\n");
setup_mq("localhost");
} else {
DLOG("Connecting to %s\n", argv[1]);
setup_mq(argv[1]);
}
if (argc < 3) {
key = "karl_loves_you";
DLOG("using default shared key of %s\n", key);
} else {
key = argv[2];
DLOG("using supplied shared key of %s\n", key);
}

while (1) {
run_tasks();
}

return (EXIT_SUCCESS);
}

59 changes: 59 additions & 0 deletions msg_signing/uglylogging.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* UglyLogging. Slow, yet another wheel reinvented, but enough to make the
* rest of our code pretty enough.
*
* Karl Palsson <karlp@remake.is>, ReMake Electric ehf. 2011
*/

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#include "uglylogging.h"

static int max_level;

int ugly_init(int level) {
max_level = level;
return 0;
}

int ugly_log(int level, const char *tag, const char *format, ...) {
if (level > max_level) {
return 0;
}
va_list args;
va_start(args, format);
time_t mytt = time(NULL);
struct tm *tt;
tt = localtime(&mytt);
fprintf(stderr, "%d-%02d-%02dT%02d:%02d:%02d ", tt->tm_year + 1900, tt->tm_mon + 1, tt->tm_mday, tt->tm_hour, tt->tm_min, tt->tm_sec);
switch (level) {
case UDEBUG:
fprintf(stderr, "DEBUG %s: ", tag);
break;
case UINFO:
fprintf(stderr, "INFO %s: ", tag);
break;
case UWARN:
fprintf(stderr, "WARN %s: ", tag);
break;
case UERROR:
fprintf(stderr, "ERROR %s: ", tag);
break;
case UFATAL:
fprintf(stderr, "FATAL %s: ", tag);
vfprintf(stderr, format, args);
exit(EXIT_FAILURE);
// NEVER GETS HERE!!!
break;
default:
fprintf(stderr, "%d %s: ", level, tag);
break;
}
vfprintf(stderr, format, args);
va_end(args);
return 1;
}
29 changes: 29 additions & 0 deletions msg_signing/uglylogging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Ugly, low performance, configurable level, logging "framework"
* Karl Palsson, ReMake Electric ehf, 2011
*/

#ifndef UGLYLOGGING_H
#define UGLYLOGGING_H

#ifdef __cplusplus
extern "C" {
#endif


#define UDEBUG 90
#define UINFO 50
#define UWARN 30
#define UERROR 20
#define UFATAL 10

int ugly_init(int level);
int ugly_log(int level, const char *tag, const char *format, ...);


#ifdef __cplusplus
}
#endif

#endif /* UGLYLOGGING_H */

0 comments on commit 6a8e875

Please sign in to comment.