logo
+7 (951) 999-89-94
428003, г. Чебоксары, ул. Федора Гладкова, д.9, оф.319

ATSHA204A — LoRa аутентификация. База

В двух предыдущих статьях мы подробно разобрали работу системы аутентификации при помощи криптомикросхемы ATSHA204A в сети беспроводной передачи данных nRF24. В этой и следующей статьях мы разберём аналогичный пример, но для популярной беспроводной технологии LoRa.

Постановка задачи

Нам нужно будет создать тестовую систему, состоящую из «базы» и «датчика», связанных по беспроводному LoRa каналу. Задача будет состоять в том, что базе нужно будет связаться с датчиком и удостовериться, что это именно «легитимный» датчик нашей экосистемы, а не клонированный хакером (чужой) датчик.

Решаться эта задача будет при помощи микросхем ATSHA204A, которые установлены на базе и датчике и соответствующим образом запрограммированы.

Оборудование

В качестве контроллеров для нашего эксперимента (и для базы и для датчика) будут использоваться беспроводные контроллеры uniSensors LoRa, которые имеют на борту всё необходимое.

  • Микроконтроллер ATmega328
  • Беспроводной LoRa модуль
  • Микросхему ATSHA204A
  • и прочие необходимые компоненты

Для работы с uniSensors LoRa и компиляции скетчей в Arduino IDE в настройках нужно выбирать контроллер Arduino Pro Mini, Atmega328 (3.3V, 8MHz).

Алгоритм работы

Алгоритм работы, обеспечивающий аутентификацию датчика, следующий: на базе микросхемой ATSHA204A генерируется качественное случайное число (RNG), затем этой же микросхемой при помощи команды MAC (на основе случайного числа и секретного ключа в Data зоне EEPROM памяти ATSHA204A) вычисляется SHA-256 хеш (дайджест). Этот дайджест запоминается для последующего сравнения с ответом датчика.

Затем ранее сгенерированное случайное число по беспроводному LoRa каналу отсылается датчику, который на его основе (при помощи идентично запрограммированной микросхемы ATSHA204A) также вычисляет SHA-256 хеш и отсылает его обратно базе для проверки и сравнения с заранее вычисленным на базе хешем.

Тот факт, что сгенерировать правильный хеш в ответ на случайное число может только «легитимный» датчик, имеющий в своём составе «правильно» запрограммированную микросхему ATSHA204A с уникальным ключом, является гарантией верной аутентификации датчика базой.

Вот графическое представление работы алгоритма аутентификации:

Более подробно об этом алгоритме и реализации его работы микросхемой ATSHA204A вы можете прочитать в предыдущих статьях этого цикла.

Базовый блок

В этой статье мы подробно рассмотрим работу базы как с криптографической частью, так и работу базы с беспроводной системой LoRa. Работу LoRa датчика мы разберём в следующей статье.

Как следует из условий задачи, базовый блок должен сгенерировать случайное число и отослать его датчику, затем сравнить вычисленный самостоятельно и полученный от датчика SHA-256 хеши. Это основной функционал базы, теперь подробнее разберём создание и работу скетча.

Код криптоалгоритма базы

Ниже приведён код криптоалгоритма базы из одной из наших предыдущих статей (там же вы можете ознакомиться с подробным описанием его работы). Здесь наша задача будет состоять в интеграции этого кода со скетчем беспроводной LoRa связи.

/* 
  ATSHA204A Project (Base)
*/

#include <sha204_library.h>

const int sha204Pin = A3;

byte challenge[MAC_CHALLENGE_SIZE];
byte hash[MAC_CHALLENGE_SIZE];

byte retCode = 0;

atsha204Class sha204(sha204Pin);


void setup() {
  Serial.begin(115200);
  Serial.println("ATSHA204 Project (Base) start...");
  
  makeRandom();
  Serial.println("Random:");
  printArray(challenge, 32);
  Serial.println();
  
  macChallenge();
  Serial.println("Hash:");
  printArray(hash, 32);
  Serial.println();
}

// Print

void printArray(byte arr[], byte len) {
  for (byte i = 0; i < len; i++) {
    if (arr[i] < 16) {Serial.print('0');}
    Serial.print(arr[i], HEX); Serial.print(' ');
  }
}

// Random

void makeRandom() {
  uint8_t response[RANDOM_RSP_SIZE];
  uint8_t tx_buffer[12] = {0};
  
  retCode = sha204.sha204m_random(tx_buffer, response, RANDOM_NO_SEED_UPDATE);

  for (byte i = 0; i < 32; i++) {
    challenge[i] = response[i];
  }
}

// MAC

void macChallenge() {
  uint8_t command[MAC_COUNT_LONG];
  uint8_t response[MAC_RSP_SIZE];

  retCode = sha204.sha204m_execute(SHA204_MAC,
                                   0, 0,
                                   MAC_CHALLENGE_SIZE, (uint8_t *) challenge,
                                   0, NULL,
                                   0, NULL,
                                   sizeof(command),   &command[0],
                                   sizeof(response), &response[0]);

  for (byte i = 0; i < 32; i++) {
    hash[i] = response[i];
  }
}

void loop() {
  
}

Подсистема LoRa

Работа с беспроводной системой LoRa является сама по себе большой и сложной темой и выходит за рамки этой статьи, поэтому здесь мы только приведём готовый LoRa скетч базы (без крипто-функций), а затем создадим финальный код скетча, который будет сочетать в себе и крипто-функционал (базы) и возможности беспроводной LoRa связи.

Для работы этого скетча и вообще для работы с LoRa подсистемой вам понадобится библиотека Arduino LoRa).

/* 
  LoRa Base
*/

#include <SPI.h>
#include <LoRa.h>

#define LORA_POWER_PIN A1

#define LORA_SS_PIN    7
#define LORA_RESET_PIN 6
#define LORA_DIO0_PIN  5

unsigned long timer = 0;

#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];

byte buffTx[PACKET_MAX_BYTES] = {
  0x23, 0x47, 0x3B, 0x1D, 0x89, 0xEA, 0x07, 0x80,
  0x07, 0x5A, 0xEE, 0xD9, 0xEC, 0x8D, 0xF5, 0x68,
  0x88, 0xAC, 0x3B, 0x36, 0x50, 0xF4, 0x03, 0xA4,
  0xDF, 0xB3, 0x38, 0xA7, 0x16, 0xBA, 0x4A, 0xF5
};

void setup() {
  Serial.begin(115200);
  Serial.println("LoRa Base");

  pinMode(LORA_POWER_PIN, OUTPUT);
  digitalWrite(LORA_POWER_PIN, LOW);
  delay(500);

  LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);

  if (!LoRa.begin(868E6)) {
    Serial.println("LoRa init failed");
    while(true);
  }
}

// Print

void printArray(byte arr[], byte len) {
  for (byte i = 0; i < len; i++) {
    if (arr[i] < 16) {Serial.print('0');}
    Serial.print(arr[i], HEX); Serial.print(' ');
  }
}

// Send

void sendPacket() {
  LoRa.beginPacket();
    LoRa.write(buffTx, PACKET_MAX_BYTES);
  LoRa.endPacket();

  Serial.print("Send:    ");
  printArray(buffTx, PACKET_MAX_BYTES);
  Serial.println();
}

// Receive

void onReceive(int packetSize) {
  if (packetSize == 0) {return;}
  if (packetSize != PACKET_MAX_BYTES) {
    Serial.println("Bad packet");
    return;
  }

  int i = -1;
  while (LoRa.available()) {
    i++;
    if (i > 31) {break;}
    buffRx[i] = LoRa.read();
  }

  Serial.print("Receive: ");
  printArray(buffRx, PACKET_MAX_BYTES);
  Serial.println();
  Serial.println();
}

void loop() {
  if (millis() - timer > 5000) {
    sendPacket();
    timer = millis();
  }
  onReceive(LoRa.parsePacket());
}

Этот скетч выполняет две простые функции: отсылает тестовый 32-байтовый массив buffTx на датчик и принимает от датчика ответный, тоже 32-байтовый массив.

Вначале мы подключаем необходимые библиотеки для работы с LoRa частью.

#include <SPI.h>
#include <LoRa.h>

Затем определяем пин включения питания радиомодуля на контроллерах uniSensors LoRa.

#define LORA_POWER_PIN A1

Также определяем пины для управления LoRa модулем.

#define LORA_SS_PIN    7
#define LORA_RESET_PIN 6
#define LORA_DIO0_PIN  5

Вводим переменную для таймера посылки пакетов в эфир.

unsigned long timer = 0;

Далее определяем 32-байтовый массив для приёма данных от датчика.

#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];

А также тестовый 32-байтовый массив для отсылки датчику.

byte buffTx[PACKET_MAX_BYTES] = {
  0x23, 0x47, 0x3B, 0x1D, 0x89, 0xEA, 0x07, 0x80,
  0x07, 0x5A, 0xEE, 0xD9, 0xEC, 0x8D, 0xF5, 0x68,
  0x88, 0xAC, 0x3B, 0x36, 0x50, 0xF4, 0x03, 0xA4,
  0xDF, 0xB3, 0x38, 0xA7, 0x16, 0xBA, 0x4A, 0xF5
};

Затем, в функции setup(), подаём питание на радиомодуль uniSensors LoRa.

  pinMode(LORA_POWER_PIN, OUTPUT);
  digitalWrite(LORA_POWER_PIN, LOW);
  delay(500);

Задаём пины управления LoRa модулем.

  LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);

И включаем LoRa модуль на частоте 868 МГц.

  if (!LoRa.begin(868E6)) {
    Serial.println("LoRa init failed");
    while(true);
  }

Далее в цикле loop() посылаем тестовый массив (в эфир) на датчик.

void loop() {
  if (millis() - timer > 5000) {
    sendPacket();
    timer = millis();
  }

И по мере их поступления принимаем данные от датчика.

  onReceive(LoRa.parsePacket());

Отсылка осуществляется функцией sendPacket():

void sendPacket() {
  LoRa.beginPacket();
    LoRa.write(buffTx, PACKET_MAX_BYTES);
  LoRa.endPacket();

  Serial.print("Send:    ");
  printArray(buffTx, PACKET_MAX_BYTES);
  Serial.println();
}

А приём пакетов от датчика осуществляется функцией onReceive().

void onReceive(int packetSize) {
  if (packetSize == 0) {return;}
  if (packetSize != PACKET_MAX_BYTES) {
    Serial.println("Bad packet");
    return;
  }

  int i = -1;
  while (LoRa.available()) {
    i++;
    if (i > 31) {break;}
    buffRx[i] = LoRa.read();
  }

  Serial.print("Receive: ");
  printArray(buffRx, PACKET_MAX_BYTES);
  Serial.println();
  Serial.println();
}

Скетч рассчитан на работу с «ответной» частью (скетчем датчика), который мы рассмотрим в следующей статье.

Далее мы создадим из этих двух (криптографического ATSHA204A и беспроводного LoRa) объединённый финальный скетч базы и подробно разберём его работу.

Скетч LoRa ATSHA204A Base

Ниже представлен полный код скетча LoRa ATSHA204A Base, объединяющий в себе криптографическую, беспроводную и функциональную части.

/* 
  LoRa ATSHA204A Base
*/

#include <SPI.h>
#include <LoRa.h>
#include <sha204_library.h>

#define LORA_POWER_PIN A1

#define LORA_SS_PIN    7
#define LORA_RESET_PIN 6
#define LORA_DIO0_PIN  5

unsigned long timer = 0;

#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];

// ATSHA204A

const int sha204Pin = A3;

byte challenge[MAC_CHALLENGE_SIZE];
byte hash[MAC_CHALLENGE_SIZE];

byte retCode = 0;

atsha204Class sha204(sha204Pin);

void setup() {
  Serial.begin(115200);
  Serial.println("LoRa ATSHA204A Base start...");

  // ATSHA204A
  makeRandom();
  Serial.print(F("Random: ")); printArray(challenge, 32); Serial.println();
  
  macChallenge();
  Serial.print(F("Hash:   ")); printArray(hash,      32); Serial.println();
  Serial.println();

  // LoRa
  pinMode(LORA_POWER_PIN, OUTPUT);
  digitalWrite(LORA_POWER_PIN, LOW);
  delay(500);

  LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);

  if (!LoRa.begin(868E6)) {
    Serial.println("LoRa init failed");
    while(true);
  }
}

// Print

void printArray(byte arr[], byte len) {
  for (byte i = 0; i < len; i++) {
    if (arr[i] < 16) {Serial.print('0');}
    Serial.print(arr[i], HEX); Serial.print(' ');
  }
}

// Compare

bool match(byte arr1[], byte arr2[], byte len) {
  bool res = true;
  for (byte i = 0; i < len; i++) {
    if (arr1[i] != arr2[i]) {
      res = false;
      break;
    }
  }
  return res;
}

// Random

void makeRandom() {
  uint8_t response[RANDOM_RSP_SIZE];
  uint8_t tx_buffer[12] = {0};
  
  retCode = sha204.sha204m_random(tx_buffer, response, RANDOM_NO_SEED_UPDATE);

  for (byte i = 0; i < 32; i++) {
    challenge[i] = response[i + 1];
  }
}

// MAC

void macChallenge() {
  uint8_t command[MAC_COUNT_LONG];
  uint8_t response[MAC_RSP_SIZE];

  retCode = sha204.sha204m_execute(SHA204_MAC,
                                   0, 0,
                                   MAC_CHALLENGE_SIZE, (uint8_t *) challenge,
                                   0, NULL,
                                   0, NULL,
                                   sizeof(command),   &command[0],
                                   sizeof(response), &response[0]);

  for (byte i = 0; i < 32; i++) {
    hash[i] = response[i + 1];
  }
}

// Send

void sendPacket() {
  LoRa.beginPacket();
    LoRa.write(challenge, PACKET_MAX_BYTES);
  LoRa.endPacket();

  Serial.print("Send:     ");
  printArray(challenge, PACKET_MAX_BYTES);
  Serial.println();
}

// Receive

void onReceive(int packetSize) {
  if (packetSize == 0) {return;}
  if (packetSize != PACKET_MAX_BYTES) {
    Serial.println("Bad packet");
    return;
  }

  int i = -1;
  while (LoRa.available()) {
    i++;
    if (i > 31) {break;}
    buffRx[i] = LoRa.read();
  }

  Serial.print(F("Response: ")); printArray(buffRx, PACKET_MAX_BYTES); Serial.println();
  Serial.print(F("Hash:     ")); printArray(hash,   PACKET_MAX_BYTES); Serial.println();
  
  if (match(hash, buffRx, 32)) {Serial.println(F("Match!"));}
                          else {Serial.println(F("Not match!"));}
  
  Serial.println();
}

void loop() {
  if (millis() - timer > 5000) {
    sendPacket();
    timer = millis();
  }
  onReceive(LoRa.parsePacket());
}

Теперь разберём работу тех частей скетча, которые мы ещё не осветили (в этой или предыдущих статьях).

В финальной версии скетча мы объявляем 32-байтовый массив для приёма хеша (дайджеста) от датчика нашей IoT экосистемы.

#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];

Объявляем 2 массива для сгенерированного случайного числа (challenge) и вычисленного на базе хеша SHA-256 (hash).

byte challenge[MAC_CHALLENGE_SIZE];
byte hash[MAC_CHALLENGE_SIZE];

Генерируем (при помощи ATSHA204A) и выводим на печать случайное 32-байтовое число.

// ATSHA204A
  makeRandom();
  Serial.print(F("Random: ")); printArray(challenge, 32); Serial.println();

Вычисляем (при помощи ATSHA204A) и выводим на печать 32-байтовый SHA-256 хеш (дайджест).

  macChallenge();
  Serial.print(F("Hash:   ")); printArray(hash,      32); Serial.println();
  Serial.println();

В цикле посылаем сгенерированное случайное число на датчик.

void loop() {
  if (millis() - timer > 5000) {
    sendPacket();
    timer = millis();
  }

В функции sendPacket() отсылаем сгенерированное микросхемой ATSHA204A случайное число и выводим его на печать.

void sendPacket() {
  LoRa.beginPacket();
    LoRa.write(challenge, PACKET_MAX_BYTES);
  LoRa.endPacket();

  Serial.print("Send:     ");
  printArray(challenge, PACKET_MAX_BYTES);
  Serial.println();
}

В функции onReceive() принимаем ответный хеш (32-байтовый дайджест) и выводим его на печать.

void onReceive(int packetSize) {
  if (packetSize == 0) {return;}
  if (packetSize != PACKET_MAX_BYTES) {
    Serial.println("Bad packet");
    return;
  }

  int i = -1;
  while (LoRa.available()) {
    i++;
    if (i > 31) {break;}
    buffRx[i] = LoRa.read();
  }

  Serial.print(F("Response: ")); printArray(buffRx, PACKET_MAX_BYTES); Serial.println();

Для наглядности также выводим вычисленный базой хеш.

  Serial.print(F("Hash:     ")); printArray(hash,   PACKET_MAX_BYTES); Serial.println();

И, наконец, сравниваем сгенерированный базой и полученный от датчика SHA-256 хеши и выносим вердикт о «легитимности» беспроводного датчика.

  if (match(hash, buffRx, 32)) {Serial.println(F("Match!"));}
                          else {Serial.println(F("Not match!"));}

Вот результат работы нашего финального скетча:

Примечание: в этом эксперименте использовались микросхемы ATSHA204A (установленные на платах uniSensors) без дополнительной прошивки и блокировки зон памяти, поэтому вместо реальных случайных чисел вы видите тестовые последовательности FF FF 00 00 FF FF 00 00, выдаваемые микросхемой ATSHA204A в этом режиме. Если вы воспользуетесь методикой программирования микросхем ATSHA204A, описанной в статьях этого цикла, то сможете записать любой секретный ключ в нулевую (0) ячейку Data зоны вашей микросхемы и (без каких-либо переделок этого скетча) получить полнофункциональную аутентификацию при помощи ATSHA204A в вашей беспроводной LoRa сети.

Заключение

Здесь мы рассмотрели только первую часть примера криптографической аутентификации при помощи микросхемы ATSHA204A в беспроводной LoRa сети (работу базы), в следующей статье мы разберём работу ответной части — беспроводного LoRa датчика.

 

Ссылки по теме

Работа с SHA-256

ATSHA204 - Обзор

ATSHA204 - Спецификации

ATSHA204 - Библиотека и примеры

ATSHA204A - Чтение зоны конфигурации 1

ATSHA204A - Чтение зоны конфигурации 2

ATSHA204A - Чтение зоны конфигурации 3

ATSHA204A - Запись конфигурации 1

ATSHA204A - Запись конфигурации 2

ATSHA204A - Запись конфигурации 3

ATSHA204A - Запись конфигурации 4

ATSHA204A - Config Lock

ATSHA204A - Работа в режиме Config Lock

ATSHA204A - Работа с зонами памяти

ATSHA204A - Запись зоны OTP

ATSHA204A - Data Lock

ATSHA204A - Чтение Data и OTP зон памяти

ATSHA204A - Команда MAC

ATSHA204A - Аутентификация. Базовый блок

ATSHA204A - Криптография и команды

ATSHA204A - Команда CheckMac

ATSHA204A - Команда Nonce

ATSHA204A - Команда GenDig

ATSHA204A - Команда HMAC

ATSHA204A - Команда DeriveKey

ATSHA204A - nRF24 аутентификация. База

ATSHA204A - nRF24 аутентификация. Датчик

ATSHA204A - LoRa аутентификация. База

ATSHA204A - LoRa аутентификация. Датчик