E4

Lightweight and secure IoT communications library

Setting up E4 clients

Previously, we've updated our application to integrate the E4 library, and protect and unprotect the exchanged messages. But we could not communicate yet, since the clients didn't hold any keys necessary to encrypt or decrypt the messages. We'll fix this now.

E4 clients can receive commands, meant to update their internal state, like the list of topic keys they can uses. So to fix our issue, we'll need to:

  • generate a topic key for /e4demo/messages topic
  • send this key to each clients, on their respective E4 receiving topics

Once clients have received the key, alice will be able to protect message she send, and unprotect messages from bob. Also, bob can protect messages he send, and unprotect alice messages.

Let's start by booting up our 2 clients, so they are listening on their topics:

# In a first terminal, start Alice client:
go run e4demo.go  -client alice -password super-secret-alice-password
# And in another, start Bob client:
go run e4demo.go  -client bob -password super-secret-bob-password

You can replace super-secret-alice-password and super-secret-bob-password with some of your choice. Now, let's write another small initKeys.go script to send SetTopicKey commands to our clients:

$ mkdir init/ && \
	touch ./init/initKeys.go
package main

import (
	"fmt"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
	e4 "github.com/teserakt-io/e4go"
	e4crypto "github.com/teserakt-io/e4go/crypto"
)

func main() {
	// Generate a key for the topic
	messageTopicKey := e4crypto.RandomKey()

	// Create Alice and Bob keys from their passwords
	aliceKey, err := e4crypto.DeriveSymKey("super-secret-alice-password")
	if err != nil {
		panic(fmt.Sprintf("failed to derivate alice key from password: %v", err))
	}
	bobKey, err := e4crypto.DeriveSymKey("super-secret-bob-password")
	if err != nil {
		panic(fmt.Sprintf("failed to derivate bob key from password: %v", err))
	}

	// Create a E4 command to set the topic key:
	setTopicKeyCmd, err := e4.CmdSetTopicKey(messageTopicKey, "/e4go/demo/messages")
	if err != nil {
		panic(fmt.Sprintf("failed to create setTopicKeyCmd: %v", err))
	}

	// Connect to MQTT broker
	opts := mqtt.NewClientOptions()
	opts.AddBroker("mqtt.eclipse.org:1338")
	opts.SetCleanSession(true)

	mqttClient := mqtt.NewClient(opts)
	timeout := time.Second
	if token := mqttClient.Connect(); token.WaitTimeout(timeout) && token.Error() != nil {
		panic(fmt.Sprintf("failed to connect to mqtt broker: %v", token.Error()))
	}

	// Protect and send the command to our 2 clients via MQTT
	if err := protectAndSendCommand(mqttClient, "alice", aliceKey, setTopicKeyCmd); err != nil {
		panic(fmt.Sprintf("failed to protect command: %v", err))
	}
	if err := protectAndSendCommand(mqttClient, "bob", bobKey, setTopicKeyCmd); err != nil {
		panic(fmt.Sprintf("failed to protect command: %v", err))
	}

	fmt.Println("Topic keys have been set!")
}

func protectAndSendCommand(mqttClient mqtt.Client, clientName string, clientKey, command []byte) error {
	protectedCommand, err := e4crypto.ProtectSymKey(command, clientKey)
	if err != nil {
		return fmt.Errorf("failed to protect command: %v", err)
	}

	clientReceivingTopic := e4.TopicForID(e4crypto.HashIDAlias(clientName))
	token := mqttClient.Publish(clientReceivingTopic, 2, true, protectedCommand)
	timeout := time.Second
	if !token.WaitTimeout(timeout) {
		return fmt.Errorf("failed to publish command: %v", token.Error())
	}

	return nil
}

Click here to download the full source of this script

Replace super-secret-alice-password and super-secret-bob-password and let's run this script:

$ go run ./init/initKeys.go
Topic key have been set for alice!
Topic key have been set for bob!

And we can observe on the client sides:

# Alice:
< received raw message on e4/a7dcef9aef26202fce82a7c7d6672afb: <raw bytes>
< unprotected message:
# Bob:
< received raw message on e4/b5d577dc9ce59725e29886632e69ecdf: <raw bytes>
< unprotected message:

We see raw binary being printed (our encrypted commands) and nothing in the unprotected message. This is good, it means our E4 clients have processed the commands successfully (otherwise an error would have been returned).

We're now ready to exchange messages!

# In alice's client:
Hello, I am alice and this is a secret message for bob!
> message published successfully

# And see in bob client:
< received raw message on /e4go/demo/messages: <raw bytes>
< unprotected message: Hello, I'm alice and this is a secret message for bob!

It works! Now let's repeat our experiment with eve, trying to intercept messages from alice:

$ go run e4demo.go  -client eve -password super-secret-eve-password
connected to mqtt.eclipse.org:1338
> subscribed to MQTT topic /e4go/demo/messages
> type anything and press enter to send the message to /e4go/demo/messages:
< received raw message on /e4go/demo/messages: <raw bytes>
failed to unprotect message: topic key not found

All good, unauthorized clients cannot unprotect the messages and read their content. alice and bob can now exchange private messages. Now, feel free to authorize eve by sending to its receiving topic a the setTopicKeyCommand!

We'll do it in the next section anyway, to simulate eve having managed to steal the topic key. And we'll see another way of using the E4 client to protect even further our messages.

Last updated on 27 Sep 2019
Published on 6 Sep 2019
Edit on GitHub