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

ATSHA204A — Библиотека и примеры

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

Тестовый стенд

В качестве тестового стенда для наших экспериментов мы будем использовать IoT контроллер uniSensors LoRa производства компании «Электромикро». Этот контроллер имеет на борту распаянный чип ATSHA204A в корпусе SOT23 с однопроводным интерфейсом SWI, подключённым на вывод A3 микроконтроллера ATmega328P.

Контроллер uniSensors LoRa кроме крипточипа ATSHA204A имеет на борту LoRa модуль, микросхему EEPROM памяти и датчик температуры и влажности Si7021, но в наших экспериментах они не будут использоваться, мы всё внимание уделим чипу ATSHA204A.

Библиотека ATSHA204A

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

Эта библиотека рассчитана на однопроводное SWI подключение чипа ATSHA204A к микроконтроллеру.

Список функций библиотеки:

atsha204Class(uint8_t pin);

uint8_t sha204c_wakeup(uint8_t *response);

uint8_t sha204c_resync(uint8_t size, uint8_t *response);	

uint8_t getSerialNumber(uint8_t *response);

uint8_t sha204m_dev_rev(uint8_t *tx_buffer, uint8_t *rx_buffer);

uint8_t sha204m_random(uint8_t * tx_buffer, uint8_t * rx_buffer, uint8_t mode);

uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);

uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);

uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);

uint8_t sha204m_check_parameters(uint8_t op_code, uint8_t param1, uint16_t param2,  uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);

Разберём подробно работу с ними и дадим необходимые пояснения по использованию в криптографических схемах защиты информации.

atsha204Class(uint8_t pin)

Конструктор, которому передаётся номер пина к которому подключён чип ATSHA204A (по однопроводному SWI интерфейсу), в нашем случае это пин A3.

atsha204Class sha204(A3);

uint8_t sha204c_wakeup(uint8_t *response)

Функция, которая проверяет находится ли чип ATSHA204A онлайн, то есть в подключённом и рабочем состоянии. В качестве аргумента ей передаётся массив, куда будет записан ответ чипа, а сама функция возвращает коды ошибок или false (0), если операция прошла успешно.

byte bufRx[SHA204_RSP_SIZE_MAX];

  byte retCode = sha204.sha204c_wakeup(bufRx);

Возвращаемые коды:

#define SHA204_SUCCESS            0x00
#define SHA204_PARSE_ERROR        0xD2
#define SHA204_CMD_FAIL           0xD3
#define SHA204_STATUS_CRC         0xD4
#define SHA204_STATUS_UNKNOWN     0xD5
#define SHA204_FUNC_FAIL          0xE0
#define SHA204_GEN_FAIL           0xE1
#define SHA204_BAD_PARAM          0xE2
#define SHA204_INVALID_ID         0xE3
#define SHA204_INVALID_SIZE       0xE4
#define SHA204_BAD_CRC            0xE5
#define SHA204_RX_FAIL            0xE6
#define SHA204_RX_NO_RESPONSE     0xE7
#define SHA204_RESYNC_WITH_WAKEUP 0xE8
#define SHA204_COMM_FAIL          0xF0
#define SHA204_TIMEOUT            0xF1

uint8_t sha204c_resync(uint8_t size, uint8_t *response)

Функция используется в случае проблем коммуникации с чипом ATSHA204A для восстановления связи с ним.

uint8_t sha204c_resync(uint8_t size, uint8_t *response);

В качестве аргументов ей передаются массив и его размер, сама функция возвращает код завершения операции.

uint8_t getSerialNumber(uint8_t *response)

Функция возвращает уникальный 72-битный (9 байт) серийный номер чипа ATSHA204A.

uint8_t getSerialNumber(uint8_t *response);

uint8_t sha204m_dev_rev(uint8_t *tx_buffer, uint8_t *rx_buffer)

Функция возвращает ревизию микросхемы ATSHA204A.

uint8_t sha204m_dev_rev(uint8_t *tx_buffer, uint8_t *rx_buffer);

uint8_t sha204m_random(uint8_t * tx_buffer, uint8_t * rx_buffer, uint8_t mode)

Функция для получения случайного числа от встроенного RNG генератора. До блокировки микросхемы ATSHA204A выдаёт вместо случайных чисел тестовую последовательность, которую нельзя применять в реальной работе криптографических функций.

uint8_t sha204m_random(uint8_t * tx_buffer, uint8_t * rx_buffer, uint8_t mode);

uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)

Функция для чтения данных из микросхемы ATSHA204A.

uint8_t sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);

В качестве аргументов ей передаются выходной массив, входной массив, идентификатор зоны памяти и адрес (смещение) внутри этой зоны, сама функция возвращает код завершения операции.

uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)

Это универсальная функция, которая предназначена для посылки информации чипу ATSHA204A и получения ответов от него, согласно разнообразным правилам и паттернам, описанным в даташите на ATSHA204A.

uint8_t sha204c_send_and_receive(uint8_t *tx_buffer,
                uint8_t rx_size, uint8_t *rx_buffer,
                uint8_t execution_delay, uint8_t execution_timeout);

где:

uint8_t *tx_buffer — буффер для отправки данных

uint8_t rx_size и uint8_t *rx_buffer — буффер и его размер для приёма ответов от чипа

uint8_t execution_delay и uint8_t execution_timeout — задержка и таймаут проведения операции

uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer)

Функция для загрузки данных в ATSHA204A и получения результатов исполнения алгоритмов, заложенных в ATSHA204A на аппаратном уровне, согласно правилам и паттернам, описанным в даташите ATSHA204A.

uint8_t sha204m_execute(uint8_t op_code,
                        uint8_t param1, uint16_t param2,
                        uint8_t datalen1, uint8_t *data1,
                        uint8_t datalen2, uint8_t *data2,
                        uint8_t datalen3, uint8_t *data3,
                        uint8_t tx_size, uint8_t *tx_buffer,
                        uint8_t rx_size, uint8_t *rx_buffer);

где:

uint8_t op_code — код операции (действия которое нужно произвести)

uint8_t param1, uint16_t param2 — опциональные параметры операции

uint8_t datalen1, uint8_t *data1 — буфер data1 и его размер

uint8_t datalen2, uint8_t *data2 — буфер data2 и его размер

uint8_t datalen3, uint8_t *data3 — буфер data3 и его размер

uint8_t tx_size и uint8_t *tx_buffer — буфер и его размер для отправки данных чипу

uint8_t rx_size и uint8_t *rx_buffer — буфер и его размер для приёма ответов от чипа

Коды операций:

#define SHA204_CHECKMAC       0x28
#define SHA204_DERIVE_KEY     0x1C
#define SHA204_DEVREV         0x30
#define SHA204_GENDIG         0x15
#define SHA204_HMAC           0x11
#define SHA204_LOCK           0x17
#define SHA204_MAC            0x08
#define SHA204_NONCE          0x16
#define SHA204_PAUSE          0x01
#define SHA204_RANDOM         0x1B
#define SHA204_READ           0x02
#define SHA204_UPDATE_EXTRA   0x20
#define SHA204_WRITE          0x12

uint8_t sha204m_check_parameters(uint8_t op_code, uint8_t param1, uint16_t param2, uint8_t datalen1, uint8_t *data1, uint8_t datalen2, uint8_t *data2, uint8_t datalen3, uint8_t *data3, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer)

Функция проверки параметров ATSHA204A и получения её результатов, согласно правилам и паттернам, описанным в даташите ATSHA204A.

uint8_t sha204m_check_parameters(uint8_t op_code,
                                 uint8_t param1, uint16_t param2,
                                 uint8_t datalen1, uint8_t *data1,
                                 uint8_t datalen2, uint8_t *data2,
                                 uint8_t datalen3, uint8_t *data3,
                                 uint8_t tx_size, uint8_t *tx_buffer,
                                 uint8_t rx_size, uint8_t *rx_buffer);

где:

uint8_t op_code — код операции (действия которое нужно произвести)

uint8_t param1, uint16_t param2 — опциональные параметры операции

uint8_t datalen1, uint8_t *data1 — буфер data1 и его размер

uint8_t datalen2, uint8_t *data2 — буфер data2 и его размер

uint8_t datalen3, uint8_t *data3 — буфер data3 и его размер

uint8_t tx_size и uint8_t *tx_buffer — буфер и его размер для отправки данных чипу

uint8_t rx_size и uint8_t *rx_buffer — буфер и его размер для приёма ответов от чипа

Примеры программирования ATSHA204A

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

Пример Check Wakeup

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

Для начала полный текст скетча:

/* 
  ATSHA204 Check Wakeup
*/

#include <sha204_library.h>

#define ATSHA204_PIN A3

atsha204Class sha204(ATSHA204_PIN);

void setup() {
  Serial.begin(115200);
  Serial.println("ATSHA204 Check Wakeup start...");

  Serial.print(F("ATSHA204A: ")); Serial.println(sha204WakeupStr(checkWakeup()));

}

String sha204WakeupStr(byte b) {
  switch (b) {
    case 1: return F("detected"); break;
   default: return F("not detected");
  }
}

bool checkWakeup() {
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte retCode = sha204.sha204c_wakeup(bufRx);
    if (!retCode) {return true;}
             else {return false;}
}

void loop() {

}

Теперь подробно разберём его работу. В начале подключаем библиотеку для работы с чипом ATSHA204A.

#include <sha204_library.h>

Затем определяем к какому пину микроконтроллера подключён чип ATSHA204A. В случае использования uniSensors LoRa в качестве тестового стенда, это пин A3.

#define ATSHA204_PIN A3

Далее инициализируем Serial интерфейс и выводим приветственное сообщение о старте нашего скетча.

void setup() {
  Serial.begin(115200);
  Serial.println("ATSHA204 Check Wakeup start...");

Потом при помощи функции checkWakeup() определяем состояние микросхемы ATSHA204A онлайн/оффлайн.

bool checkWakeup() {
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte retCode = sha204.sha204c_wakeup(bufRx);
    if (!retCode) {return true;}
             else {return false;}
}

Для этого объявляем байтовый массив bufRx размера SHA204_RSP_SIZE_MAX.

  byte bufRx[SHA204_RSP_SIZE_MAX];

Константа SHA204_RSP_SIZE_MAX определена в библиотеке и равна максимально возможной длине ответа микросхемы ATSHA204A. Сам запрос состояния чипа ATSHA204A выполняется библиотечной функцией sha204.sha204c_wakeup().

sha204.sha204c_wakeup(bufRx)

И затем при помощи функции sha204WakeupStr()

String sha204WakeupStr(byte b) {
  switch (b) {
    case 1: return F("detected"); break;
   default: return F("not detected");
  }
}

выводим сообщение о статусе микросхемы ATSHA204A в Serial.

  Serial.print(F("ATSHA204A: "));
  Serial.println(sha204WakeupStr(checkWakeup()));

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

Система установила, что микросхема ATSHA204A присутствует на плате и находится в режиме онлайн, то есть может принимать команды и выдавать результаты запросов.

Пример Check Serial

В этом примере работы с чипом ATSHA204A мы попробуем получить серийный номер нашего экземпляра ATSHA204A. Документация говорит нам о том, что каждый чип ATSHA204A имеет уникальный и не повторяющийся среди всех микросхем семейства 72-битный серийный номер.

Полный код скетча:

/* 
  ATSHA204 Check Serial
*/

#include <sha204_library.h>

#define ATSHA204_PIN A3

atsha204Class sha204(ATSHA204_PIN);

void setup() {
  Serial.begin(115200);
  Serial.println("ATSHA204 Check Serial start...");

  Serial.print(F("Serial: "));
  
  byte buffSn[9];
  if (!sha204Serial(buffSn)) {
    Serial.println(F("failed"));
  } else {
    printBuffer(buffSn, 9);
  }
}

void printBuffer(byte* data, byte len) {
  for (size_t i = 0; i < len; i++) {
    if (data[i] < 10) {Serial.print('0');}
    Serial.print(data[i], HEX);
    if (i < len - 1) {Serial.print(' ');}
  }
}

bool sha204Serial(byte* data) {
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte ret_code = sha204.getSerialNumber(bufRx);
  if (!ret_code) {
    memcpy(data, bufRx, 9);
    return true;
  } else {
    return false;
  }
}

void loop() {

}

Основную работу по коммуникации с чипом ATSHA204A, получению от него серийного номера и определению статуса операции выполняет функция sha204Serial().

bool sha204Serial(byte* data) {
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte ret_code = sha204.getSerialNumber(bufRx);
  if (!ret_code) {
    memcpy(data, bufRx, 9);
    return true;
  } else {
    return false;
  }
}

В которой: определяем байтовый массив для приёма данных.

  byte bufRx[SHA204_RSP_SIZE_MAX];

Получаем данные о серийном номере и код завершения операции.

  byte ret_code = sha204.getSerialNumber(bufRx);

Анализируем результат коммуникации с чипом ATSHA204A и в случае успеха (ret_code == 0) копируем байты серийного номера в массив data, переданный нам в качестве аргумента функции sha204Serial() и возвращаем логический результат проведения операции получения серийного номера (true/false).

  if (!ret_code) {
    memcpy(data, bufRx, 9);
    return true;
  } else {
    return false;
  }

И в завершение выводим в Serial серийный номер нашей микросхемы ATSHA204A.

  Serial.print(F("Serial: "));
  
  byte buffSn[9];
  if (!sha204Serial(buffSn)) {
    Serial.println(F("failed"));
  } else {
    printBuffer(buffSn, 9);
  }

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

Обратите внимание: несмотря на то, что микросхема ATSHA204A возвращает 9-байтовый (72 бита) серийный номер, но по настоящему уникальными в нём являются только 6 байт (48 бит), эти байты на скриншоте выделены красным. Остальные байты повторяются у всех микросхем семейства ATSHA204A.

Пример Check Lock

Следующим примером будет получение статуса микросхемы ATSHA204A заблокирована/не заблокирована (lock/unlock). Если вы ознакомились с вводными статьями этой документации по ATSHA204A, то знаете о возможности (необходимости) блокировки микросхемы ATSHA204A для её нормальной работы. Пример ниже позволяет узнать статус вашей микросхемы ATSHA204A — заблокирована она или нет.

Полный код примера:

/* 
  ATSHA204 Check Lock
*/

#include <sha204_library.h>

#define ATSHA204_PIN A3

#define POS_LOCK 3

#define LOCK_UNK 0
#define LOCK_YES 1
#define LOCK_NOT 2

atsha204Class sha204(ATSHA204_PIN);

void setup() {
  Serial.begin(115200);
  Serial.println("ATSHA204 Check Lock start...");

  Serial.print(F("Lock: ")); Serial.println(sha204LockedStr(sha204Locked()));
}

String sha204LockedStr(byte b) {
  switch (b) {
    case LOCK_YES: return F("yes"); break;
    case LOCK_NOT: return F("no");  break;
          default: return F("unknown");
  }
}

byte sha204Locked() {
  byte bufTx[SHA204_CMD_SIZE_MAX];
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, 0x15<<2);
  
  if (!retCode) {
    if (bufRx[SHA204_BUFFER_POS_DATA + POS_LOCK] == 0x00) {return LOCK_YES;}
                                                     else {return LOCK_NOT;}
  } else {
    return LOCK_UNK;
  }
}

void loop() {

}

Теперь подробно разберём этот пример. Вначале определяем смещение данных о статусе (lock/unlock) микросхемы ATSHA204A в приёмном RX буфере.

#define POS_LOCK 3

Затем определяем три возможных варианта статуса — не заблокирована, заблокирована или состояние неизвестно.

#define LOCK_UNK 0
#define LOCK_YES 1
#define LOCK_NOT 2

Далее, в функции sha204Locked(), определяем статус (lock/unlock) микросхемы ATSHA204A.

byte sha204Locked() {
  byte bufTx[SHA204_CMD_SIZE_MAX];
  byte bufRx[SHA204_RSP_SIZE_MAX];
  
  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, 0x15<<2);
  
  if (!retCode) {
    if (bufRx[SHA204_BUFFER_POS_DATA + POS_LOCK] == 0x00) {return LOCK_YES;}
                                                     else {return LOCK_NOT;}
  } else {
    return LOCK_UNK;
  }
}

Вначале функции определяем два байтовых массива для передачи (TX) и приёма (RX) данных.

  byte bufTx[SHA204_CMD_SIZE_MAX];
  byte bufRx[SHA204_RSP_SIZE_MAX];

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

  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, 0x15<<2);

Далее анализируем полученный результат и возвращаем соответствующее значение статуса заблокирована, не заблокирована или состояние неизвестно.

  if (!retCode) {
    if (bufRx[SHA204_BUFFER_POS_DATA + POS_LOCK] == 0x00) {return LOCK_YES;}
                                                     else {return LOCK_NOT;}
  } else {
    return LOCK_UNK;
  }

И затем при помощи функции sha204LockedStr( )

String sha204LockedStr(byte b) {
  switch (b) {
    case LOCK_YES: return F("yes"); break;
    case LOCK_NOT: return F("no");  break;
          default: return F("unknown");
  }
}

выводим сообщение о статусе (lock/unlock) микросхемы ATSHA204A в Serial.

  Serial.print(F("Lock: "));
  Serial.println(sha204LockedStr(sha204Locked()));

Вот результат работы скетча:

Система определила, что наша микросхема ATSHA204A не заблокирована и сообщила нам об этом.

Заключение

В этой статье мы рассмотрели методы и приёмы программирования микросхемы ATSHA204A при помощи специализированной библиотеки в среде Arduino IDE. Эта информация поможет вам сделать первые шаги в интересном и очень актуальном направлении применения криптографии в проектах на микроконтроллерах.

Что важно понимать: успешная криптозащита ваших проектов требует хорошего владения программированием (и программированием микросхемы ATSHA204A в частности), а также хорошего понимания собственно работы криптографических алгоритмов и схем защиты информации.

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

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

Работа с SHA-256

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 - Криптография и команды

ATSHA204A - Команда CheckMac

ATSHA204A - Команда Nonce

ATSHA204A - Команда GenDig

ATSHA204A - Команда HMAC

ATSHA204A - Команда DeriveKey

ATSHA204A - Команда DeriveKey

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

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

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

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