В этой статье мы познакомимся с приёмами программирования микросхемы 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 и о работе криптографических схем защиты в наших следующих статьях.
Ссылки по теме
ATSHA204A - Чтение зоны конфигурации 1
ATSHA204A - Чтение зоны конфигурации 2
ATSHA204A - Чтение зоны конфигурации 3
ATSHA204A - Запись конфигурации 1
ATSHA204A - Запись конфигурации 2
ATSHA204A - Запись конфигурации 3
ATSHA204A - Запись конфигурации 4
ATSHA204A - Работа в режиме Config Lock
ATSHA204A - Работа с зонами памяти
ATSHA204A - Чтение Data и OTP зон памяти
ATSHA204A - Аутентификация. Базовый блок
ATSHA204A - Аутентификация. Датчик
ATSHA204A - Криптография и команды
ATSHA204A - nRF24 аутентификация. База
ATSHA204A - nRF24 аутентификация. Датчик