ESP32: ESP-NOW Encrypted Messages

In this guide, you’ll discover how to secure ESP-NOW messages exchanged in between ESP32 boards. ESP-NOW utilizes the CCMP technique for encryption making use of a Primary Master Key (PMK) and Local Master Keys (LMK).

ESP32 ESP-NOW Encrypted Messages Arduino IDE

If you’re brand-new to ESP-NOW, we recommend reviewing the adhering to getting going guide first to obtain acquainted with ESP-NOW concepts as well as functions on the ESP32:

CCMP Security Protocol

CCMP suggests Counter Mode with Cipher Block Chaining Message Authentication Code Protocol. This is a security protocol designed for Wireless LAN. ESP-NOW can use the CCMP method to encrypt messages.

Accordingly to the:

“ESP-NOW use CCMP approach which can be referenced in IEEE Std. 802.11-2012 to shield the vendor-specific activity structure.”

The Wi-Fi gadget keeps a Primary Master Key (PMK) and also several Local Master Keys (LMK). The size of the keys is 16 bytes.

Primary Master Key (PMK)

PMK is used to secure LMK with the AES-128 algorithm. To set the PMK trick of the Wi-Fi tool, you can use the esp_now_set_pmk() function to set PMK. If PMK is not established, a default PMK will be used.

Local Master Key (LMK)

You must set the LMK of the combined tool to secure the vendor-specific action structure with CCMP technique. The optimum number of different LMKs is six. The LMK is a residential property of the peer, esp_now_peer_info_t item, and also can be established on the lmk residential property as we’ll see later on.

ESP32: Getting Board MAC Address

To connect by means of ESP-NOW, you need to understand the MAC Addresses of the boards so that you can add each various other as peers.

Each ESP32 has an and that’s just how we identify each board (discover exactly how to ).

To obtain your board’s MAC Address, upload the complying with code.

// Complete Instructions to Get and Change ESP MAC Address: https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/

#include "WiFi.h"
 
void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

After publishing the code, open up the Serial Monitor at a baud rate of 115200 and push the ESP32 RST/EN switch. The MAC address need to be printed as follows:

ESP-NOW ESP32 Getting Board MAC Address

Save your board MAC address because you’ll require it in the following ESP-NOW examples.

Project Overview

The instance we’ll show you is extremely basic to make sure that you can comprehend just how to encrypt your ESP-NOW messages. The sender will send out a structure which contains two arbitrary numbers, x and also y, and a counter variable (to track the variety of sent packages).

ESP32 ESP-NOW Encrypted Messages Project Overview

Here are the major steps:

  1. The sender sets its PMK;
  2. The sender adds the receiver as a peer and sets its LMK;
  3. The receiver sets its PMK (should be the same of the receiver);
  4. The receiver adds the sender as a peer and sets its LMK (should be the same as the one set on the sender board);
  5. The sender sends the following structure to the receiver board:
typedef struct struct_message {
    int counter;
    int x;
    int y;
} struct_message;
  1. The receiver gets the message.

ESP32 Sender Sketch (ESP-NOW Encrypted)

Here’s the code for the ESP32 Sender board. Duplicate the code to your Arduino IDE, however do not upload it. You require to make a couple of modifications to make it help you.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/?s=esp-now
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH THE RECEIVER'S MAC Address
uint8_t receiverAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// PMK and LMK keys
static const char* PMK_KEY_STR = "REPLACE_WITH_PMK_KEY";
static const char* LMK_KEY_STR = "REPLACE_WITH_LMK_KEY";

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
    int counter;
    int x;
    int y;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// Counter variable to keep track of number of sent packets
int counter;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("/r/nLast Packet Send Status:/t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  
  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("There was an error initializing ESP-NOW");
    return;
  }
  
  // Set PMK key
  esp_now_set_pmk((uint8_t *)PMK_KEY_STR);
  
  // Register the receiver board as peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, receiverAddress, 6);
  peerInfo.channel = 0;
  //Set the receiver device LMK key
  for (uint8_t i = 0; i < 16; i++) {
    peerInfo.lmk[i] = LMK_KEY_STR[i];
  }
  // Set encryption to true
  peerInfo.encrypt = true;
  
  // Add receiver as peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of transmitted packet
  esp_now_register_send_cb(OnDataSent);
}
void loop() {
  static unsigned long lastEventTime = millis();
  static const unsigned long EVENT_INTERVAL_MS = 5000;
  if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
    lastEventTime = millis();
    
    // Set values to send
    myData.counter = counter++;
    myData.x = random(0,50);
    myData.y = random(0,50);
  
    // Send message via ESP-NOW
    esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) &myData, sizeof(myData));
    if (result == ESP_OK) {
      Serial.println("Sent with success");
    }
    else {
      Serial.println("Error sending the data");
    }  
  }
}

Don’t neglect you require to include the receiver’s MAC address in the code. In my instance, the receiver MAC address is 30: AE: A4:07:0 D:64. It will certainly look as follows on the code:

// REPLACE WITH THE RECEIVER'S MAC Address
uint8_t receiverAddress[] = {0x30, 0xAE, 0xA4, 0x07, 0x0D, 0x64};

Let’s take an appearance at the relevant parts of code that deal with security.

Create the PMK as well as LMK keys for this tool on the following lines. It can be made from numbers as well as letters and also the tricks are 16 bytes (you can search online for “on-line byte counter” to inspect the length of your keys).

static const char* PMK_KEY_STR = "REPLACE_WITH_PMK_KEY";
static const char* LMK_KEY_STR = "REPLACE_WITH_LMK_KEY";

For instance, the trick can be something such as this 00XXmkwei/lpP Çf.

The sender and also receiver must have the exact same PMK and also LMK tricks.

Set the tool PMK secret utilizing the esp_now_set_pmk() function as follows:

esp_now_set_pmk((uint8_t *)PMK_KEY_STR);

The LMK is a building of the peer tool, so you need to set it when you sign up a tool as peer. You established the LMK as adheres to:

for (uint8_t i = 0; i < 16; i++) {
   peerInfo.lmk[i] = LMK_KEY_STR[i];
}

You additionally need to establish the encrypt peer home as real.

peerInfo.encrypt = true;

And that’s it. This is all you need to do to encrypt ESP-NOW messages. Now, you can make use of the ESP-NOW feature to exchange information as well as the messages will be secured.

ESP32 Receiver Sketch (ESP-NOW Encrypted Messages)

Here’s the code for the ESP32 Receiver board. Duplicate the code to your Arduino IDE, yet do not submit it yet. You need to make a few adjustments to make it work for you.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/?s=esp-now
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <WiFi.h>

// REPLACE WITH YOUR MASTER MAC Address
uint8_t masterMacAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// PMK and LMK keys
static const char* PMK_KEY_STR = "REPLACE_WITH_PMK_KEY";
static const char* LMK_KEY_STR = "REPLACE_WITH_LMK_KEY";

// Structure example to send data
// Must match the sender structure
typedef struct struct_message {
    int counter; // must be unique for each sender board
    int x;
    int y;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// Function to print MAC address on Serial Monitor
void printMAC(const uint8_t * mac_addr){
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
}

// Callback function executed when data is received
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {
 
  Serial.print("Packet received from: ");
  printMAC(mac_addr);
  
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Packet number: ");
  Serial.println(myData.counter);
  Serial.print("X: ");
  Serial.println(myData.x);
  Serial.print("Y: ");
  Serial.println(myData.y);
}
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  
  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("There was an error initializing ESP-NOW");
    return;
  }
  
  // Set the PMK key
  esp_now_set_pmk((uint8_t *)PMK_KEY_STR);
  
  // Register the master as peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, masterMacAddress, 6);
  peerInfo.channel = 0;
  // Setting the master device LMK key
  for (uint8_t i = 0; i < 16; i++) {
    peerInfo.lmk[i] = LMK_KEY_STR[i];
  }
  // Set encryption to true
  peerInfo.encrypt = true;
  
  // Add master as peer       
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
  
}

You need to add the sender board as a peer. So, you need to know its MAC address. Add the sender MAC address in the complying with line:

uint8_t masterMacAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

Set the PMK as well as LMK keys. Need to coincide as the various other board.

static const char* PMK_KEY_STR = "REPLACE_WITH_PMK_KEY";
static const char* LMK_KEY_STR = "REPLACE_WITH_LMK_KEY";

Set the gadget PMK trick utilizing the esp_now_set_pmk() feature as follows:

esp_now_set_pmk((uint8_t *)PMK_KEY_STR);

The LMK is a residential property of the peer gadget, so you need to set it when you sign up a tool as peer. You established the LMK as complies with:

for (uint8_t i = 0; i < 16; i++) {
  peerInfo.lmk[i] = LMK_KEY_STR[i];}

You likewise need to establish the encrypt peer property as true.

peerInfo.encrypt = true;

And that’s it, currently the receiver board can receiver and decrypt the encrypted messages sent by the sender.

Demonstration

Upload the codes to the matching boards.

Open the Serial Monitor to check what’s going on. You can make use of PuTTY to be able to see the messages on both boards all at once.

This is what you should jump on the receiver board:

ESP-NOW Receiver Encrypted Messages PUTTY

On the sender board, you should obtain “Delivery Success” messages.

ESP-NOW Sender Encrypted Messages Delivery Success Serial Monitor

Wrapping Up

In this tutorial, you discovered how to encrypt ESP-NOW messages using PMK and LMK keys.

I evaluated the encryption in various circumstances and here are the outcomes:

  • Sender and receiver encrypted with same keys: receiver board receives the messages successfully;
  • The sender sends encrypted messages, but the receiver doesn’t have the keys or has different keys: the receiver doesn’t get the messages;
  • The receiver has the code for encryption but the sender doesn’t: the receiver gets the messages anyway. I don’t think this is the behavior we expected. Since we add the encryption code on both boards, one would expect that if the receiver board got a message that is not encrypted, it would ignore it. But that’s not what happens. It receives all messages, encrypted and not encrypted. At the moment, there isn’t a way to know if the received message is encrypted or not, which seems like a limitation at the moment.

We hope you discovered this tutorial valuable. We have a lot more ESP-NOW instances you might like:

If you would certainly such as to discover more regarding the ESP32 board and also IoT, see to it you have a look at our resources:

Thanks for reading.