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

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

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

Таблица зоны конфигурации

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

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

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

Для начала создадим скетч, который читает данные из конфигурационной зоны вашего экземпляра микросхемы ATSHA204A и выводит его в Serial. Полный код скетча ATSHA204 Read Config:

/* 
  ATSHA204 Read Config
*/

#include <sha204_library.h>

#define ATSHA204_PIN A3

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

String func[] = {"[R] Serial",
                 "[R] Revision",
                 "[R] Serial",
                 "[R] I2C Enable",
                 "[W] Misk",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[E] Extra"};

#define BUF_READ_LEN 4
byte bufRead[BUF_READ_LEN];

atsha204Class sha204(ATSHA204_PIN);

void setup() {
  Serial.begin(115200);
  Serial.println(F("ATSHA204 Read Config..."));

  read();
}

// Print

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

// Read

byte addr(byte slot) {
  return slot * 4;
}

void readConf(byte slot) {
  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, addr(slot));
  
  bufRead[0] = bufRx[SHA204_BUFFER_POS_DATA + 0];
  bufRead[1] = bufRx[SHA204_BUFFER_POS_DATA + 1];
  bufRead[2] = bufRx[SHA204_BUFFER_POS_DATA + 2];
  bufRead[3] = bufRx[SHA204_BUFFER_POS_DATA + 3];
}

void read() {
  for (byte i = 0; i < 22; i++) {
    if (i == 0) {Serial.println(F("Readable:"));}
    if (i == 4) {Serial.println(F("\nWritable:"));}
    if (i == 5) {Serial.println();}
    if (i == 13) {Serial.println();}
    if (i == 17) {Serial.println();}
    if (i == 21) {Serial.println(F("\nExtra:"));}
    Serial.print(F("Slot "));
    Serial.print(i < 10 ? "0" : "");
    Serial.print(i);
    Serial.print(F(": "));
    readConf(i);
    printBuffer(bufRead, BUF_READ_LEN);
    Serial.print(' ');
    Serial.print(func[i]);
    Serial.println();
  }
}

void loop() {

}

Теперь подробнее разберём его работу. Наша задача прочитать всё содержимое конфигурационной зоны и вывести его в Serial в удобочитаемом виде. Для этого создаём массив func с пояснительными надписями для каждого слота конфигурационной зоны ATSHA204A. Всего слотов, как вы могли заметить, 22.

String func[] = {"[R] Serial",
                 "[R] Revision",
                 "[R] Serial",
                 "[R] I2C Enable",
                 "[W] Misk",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[E] Extra"};

В квадратных скобках для удобства указан тип слота: доступный для только чтения [R], доступный для записи [W] и доступный только для изменения специальной Extra командой [E]. Само назначение типов данных должно быть вам понятно из прочитанного вами содержания предыдущих статей.

Далее определяем размер (4 байта) и вводим буфер для чтения слотов.

#define BUF_READ_LEN 4
byte bufRead[BUF_READ_LEN];

Чтение данных из микросхемы ATSHA204A осуществляется функцией readConf()

void readConf(byte slot) {
  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, addr(slot));
  
  bufRead[0] = bufRx[SHA204_BUFFER_POS_DATA + 0];
  bufRead[1] = bufRx[SHA204_BUFFER_POS_DATA + 1];
  bufRead[2] = bufRx[SHA204_BUFFER_POS_DATA + 2];
  bufRead[3] = bufRx[SHA204_BUFFER_POS_DATA + 3];
}

где функцией

byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, addr(slot));

читается каждый 4-байтовый слот и далее этими данными заполняется массив bufRead

  bufRead[0] = bufRx[SHA204_BUFFER_POS_DATA + 0];
  bufRead[1] = bufRx[SHA204_BUFFER_POS_DATA + 1];
  bufRead[2] = bufRx[SHA204_BUFFER_POS_DATA + 2];
  bufRead[3] = bufRx[SHA204_BUFFER_POS_DATA + 3];

Перебор всех 22-х слотов конфигурационной зоны осуществляется циклом

  for (byte i = 0; i < 22; i++) {
    ...
  }

И таким образом функция read() выводит всё содержимое конфигурационной зоны с необходимым форматированием и пояснениями.

void read() {
  for (byte i = 0; i < 22; i++) {
    if (i == 0) {Serial.println(F("Readable:"));}
    if (i == 4) {Serial.println(F("\nWritable:"));}
    if (i == 5) {Serial.println();}
    if (i == 13) {Serial.println();}
    if (i == 17) {Serial.println();}
    if (i == 21) {Serial.println(F("\nExtra:"));}
    Serial.print(F("Slot "));
    Serial.print(i < 10 ? "0" : "");
    Serial.print(i);
    Serial.print(F(": "));
    readConf(i);
    printBuffer(bufRead, BUF_READ_LEN);
    Serial.print(' ');
    Serial.print(func[i]);
    Serial.println();
  }
}

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

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

Запись конфигурационной зоны

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

/* 
  ATSHA204 Write Config
*/

#include <sha204_library.h>

#define ATSHA204_PIN A3

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

String func[] = {"[R] Serial",
                 "[R] Revision",
                 "[R] Serial",
                 "[R] I2C Enable",
                 "[W] Misk",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Slot Config",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Use Flag/Update Count",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[W] Last Key Use",
                 "[E] Extra"};

#define BUF_READ_LEN 4
byte bufRead[BUF_READ_LEN];

byte news[17][4] = {
  // Misk
  {0x55, 0x00, 0x55, 0x00},
  
  // Slot Config 
  {0x8F, 0x80, 0x80, 0xA1},
  {0x82, 0xE0, 0xA3, 0x60},
  {0x94, 0x40, 0xA0, 0x85},
  {0x86, 0x40, 0x87, 0x07},
  {0x0F, 0x00, 0x89, 0xF2},
  {0x8A, 0x7A, 0x0B, 0x8B},
  {0x0C, 0x4C, 0xDD, 0x4D},
  {0xC2, 0x42, 0xAF, 0x8F},
  
  // Use Flag/Update Count 
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  
  // Last Key Use
  {0x00, 0x01, 0x02, 0x03},
  {0x04, 0x05, 0x06, 0x07},
  {0x08, 0x09, 0x0A, 0x0B},
  {0x0C, 0x0D, 0x0E, 0x0F},
};

byte defs[17][4] = {
  {0xC8, 0x00, 0x55, 0x00},
  {0x8F, 0x80, 0x80, 0xA1},
  {0x82, 0xE0, 0xA3, 0x60},
  {0x94, 0x40, 0xA0, 0x85},
  {0x86, 0x40, 0x87, 0x07},
  {0x0F, 0x00, 0x89, 0xF2},
  {0x8A, 0x7A, 0x0B, 0x8B},
  {0x0C, 0x4C, 0xDD, 0x4D},
  {0xC2, 0x42, 0xAF, 0x8F},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
};

#define BUF_WRITE_LEN 4
byte bufWrite[BUF_WRITE_LEN];

atsha204Class sha204(ATSHA204_PIN);

void setup() {
  Serial.begin(115200);
  Serial.println(F("ATSHA204 Write Config..."));

  Serial.println("\n========== Old ==========");
  read();

  write();

  Serial.println("\n========== New ==========");
  read();
}

// Print

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

// Read

byte addr(byte slot) {
  return slot * 4;
}

void readConf(byte slot) {
  byte retCode = sha204.sha204m_read(bufTx, bufRx, SHA204_ZONE_CONFIG, addr(slot));
  
  bufRead[0] = bufRx[SHA204_BUFFER_POS_DATA + 0];
  bufRead[1] = bufRx[SHA204_BUFFER_POS_DATA + 1];
  bufRead[2] = bufRx[SHA204_BUFFER_POS_DATA + 2];
  bufRead[3] = bufRx[SHA204_BUFFER_POS_DATA + 3];
}

void read() {
  for (byte i = 0; i < 22; i++) {
    if (i == 0) {Serial.println(F("Readable:"));}
    if (i == 4) {Serial.println(F("\nWritable:"));}
    if (i == 5) {Serial.println();}
    if (i == 13) {Serial.println();}
    if (i == 17) {Serial.println();}
    if (i == 21) {Serial.println(F("\nExtra:"));}
    Serial.print(F("Slot "));
    Serial.print(i < 10 ? "0" : "");
    Serial.print(i);
    Serial.print(F(": "));
    readConf(i);
    printBuffer(bufRead, BUF_READ_LEN);
    Serial.print(' ');
    Serial.print(func[i]);
    Serial.println();
  }
}

// Write

void setNew(byte row) {
  bufWrite[0] = news[row][0];
  bufWrite[1] = news[row][1];
  bufWrite[2] = news[row][2];
  bufWrite[3] = news[row][3];
}

void setDef(byte row) {
  bufWrite[0] = defs[row][0];
  bufWrite[1] = defs[row][1];
  bufWrite[2] = defs[row][2];
  bufWrite[3] = defs[row][3];
}

void writeConfig(byte slt) {
  byte retCode = sha204.sha204m_execute(SHA204_WRITE,
                                        SHA204_ZONE_CONFIG, slt,
                                        4, bufWrite,
                                        0, NULL,
                                        0, NULL,
                                        WRITE_COUNT_SHORT, bufTx,
                                        WRITE_RSP_SIZE,    bufRx);
}

void write() {
  for (byte i = 0; i < 17; i++) {
    setNew(i);
    //setDef(i);
    writeConfig(i + 4);
  }
}

void loop() {

}

И теперь полный разбор его работы, за исключением частей, которые были объяснены ранее. Вначале объявляем двумерный массив news

byte news[17][4] = {
  // Misk
  {0x55, 0x00, 0x55, 0x00},
  
  // Slot Config 
  {0x8F, 0x80, 0x80, 0xA1},
  {0x82, 0xE0, 0xA3, 0x60},
  {0x94, 0x40, 0xA0, 0x85},
  {0x86, 0x40, 0x87, 0x07},
  {0x0F, 0x00, 0x89, 0xF2},
  {0x8A, 0x7A, 0x0B, 0x8B},
  {0x0C, 0x4C, 0xDD, 0x4D},
  {0xC2, 0x42, 0xAF, 0x8F},
  
  // Use Flag/Update Count 
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  {0x55, 0x05, 0x55, 0x05},
  
  // Last Key Use
  {0x00, 0x01, 0x02, 0x03},
  {0x04, 0x05, 0x06, 0x07},
  {0x08, 0x09, 0x0A, 0x0B},
  {0x0C, 0x0D, 0x0E, 0x0F},
};

где 17 — это количество слотов в конфигурационной зоне, доступных для записи, а 4 — это количество байтов в одном слоте. И записываем в этот массив байты в соответствии с крипто-архитектурой вашего проекта. Смысл и назначение каждого байта были подробно объяснены в предыдущих статьях, если вам что-то непонятно, то рекомендуем ещё раз внимательно их прочитать.

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

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

byte defs[17][4] = {
  {0xC8, 0x00, 0x55, 0x00},
  {0x8F, 0x80, 0x80, 0xA1},
  {0x82, 0xE0, 0xA3, 0x60},
  {0x94, 0x40, 0xA0, 0x85},
  {0x86, 0x40, 0x87, 0x07},
  {0x0F, 0x00, 0x89, 0xF2},
  {0x8A, 0x7A, 0x0B, 0x8B},
  {0x0C, 0x4C, 0xDD, 0x4D},
  {0xC2, 0x42, 0xAF, 0x8F},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0x00, 0xFF, 0x00},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
  {0xFF, 0xFF, 0xFF, 0xFF},
};

Это может вам понадобиться для восстановления исходного состояния памяти микросхемы во время экспериментов с ней.

И объявляем массив из 4-х байтов для записи слотов.

#define BUF_WRITE_LEN 4
byte bufWrite[BUF_WRITE_LEN];

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

  Serial.println("\n========== Old ==========");
  read();

  write();

  Serial.println("\n========== New ==========");
  read();

Далее, функция write()

void write() {
  for (byte i = 0; i < 17; i++) {
    setNew(i);
    //setDef(i);
    writeConfig(i + 4);
  }
}

производит перебор всех 17-и слотов, доступных для записи и один за другим меняет содержимое этих слотов на нужное нам. Функция setNew()

void setNew(byte row) {
  bufWrite[0] = news[row][0];
  bufWrite[1] = news[row][1];
  bufWrite[2] = news[row][2];
  bufWrite[3] = news[row][3];
}

формирует новый слот и функция writeConfig()

void writeConfig(byte slt) {
  byte retCode = sha204.sha204m_execute(SHA204_WRITE,
                                        SHA204_ZONE_CONFIG, slt,
                                        4, bufWrite,
                                        0, NULL,
                                        0, NULL,
                                        WRITE_COUNT_SHORT, bufTx,
                                        WRITE_RSP_SIZE,    bufRx);
}

непосредственно осуществляет запись. Таким образом содержимое массива news переносится в память микросхемы ATSHA204A.

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

В результате мы успешно «прошили» конфигурационную зону микросхемы ATSHA204A и можем переходить к следующим этапам работы с ней.

Заключение

На этом мы заканчиваем цикл статей о чтении и записи конфигурационной зоны микросхемы ATSHA204A и переходим к следующиму этапу работы с ней — «закрыванию» (Lock) конфигурационной зоны ATSHA204A и работе с зоной данных (Data Zone) этой микросхемы.

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

Работа с SHA-256

ATSHA204 - Обзор

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

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

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

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

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

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

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

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

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 аутентификация. Датчик