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

Работа с SHA-256

В этой статье мы поговорим об алгоритме хеширования SHA-256, подробно разберём что это такое, для чего нужно и как это работает. Повествование будет вестись в стиле «SHA-256 — это не просто, а очень просто» и рассчитано на неподготовленных пользователей и любителей электроники и программирования. В завершающей части статьи будут даны практические примеры работы с алгоритмом хеширования SHA-256 на микроконтроллерах в среде разработки Arduino IDE.

Криптография и DIY

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

Поэтому в этой статье мы максимально просто и доходчиво объясним что такое SHA-256 и как работать на практике с этим алгоритмом хеширования, так, чтобы стало понятно даже самому неподготовленному читателю.

Ради простоты изложения будут опущены многие «заумные» подробности и всё будет изложено максимально простым языком. Те, кому нужны подробности, могут более детально ознакомиться с этой темой на множестве ресурсов в интернете.

Что такое хеширование

Хеширование — это алгоритм, который преобразует исходные данные (любого объёма) в небольшой набор выходных данных фиксированной длины. Входными данными может быть одно слово или целый роман, а выходными будет набор из нескольких байт. Важным для нас в этом преобразовании являются:

  1. Длина выходного сообщения (хеша) всегда одинакова, независимо от длины входных данных
  2. Любые, даже самые минимальные изменения в исходном сообщении приводят к генерации уникального хеша
  3. Практическая невозможность восстановления исходного сообщения по известному хешу

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

Что важно понимать: SHA-256 хотя и криптографический алгоритм, но он не предназначен для шифрования сообщений, для этого есть другие алгоритмы, например AES-128. SHA-256 предназначен исключительно для создания хешей и контроля целостности информации. А связка шифрующих алгоритмов с SHA-256 в практических приложениях позволяет создавать надёжно защищённые коммуникации.

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

SHA-256

SHA-256 — это реализация одного из множества возможных алгоритмов хеширования. Благодаря своей (относительной) простоте, достаточной надёжности и криптостойкости, а также приемлемой скорости работы, SHA-256 получил широкое распространение в современном мире. Он используется в телекоммуникациях, интернете, для шифрования беспроводных пакетов в IoT и т. д. и т. п. В общем, это один из наиболее широко используемых алгоритмов хеширования.

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

В практическом плане для нас важно знать, что выходное сообщение (хеш) алгоритма SHA-256 составляет 32 байта или 256 бит, собственно отсюда и цифра «256» в его названии.

Для чего нам может понадобится SHA-256 (в связке с другими алгоритмами, шифрованием и т. д.):

  • Для защиты передачи беспроводных пакетов в IoT
  • Для защиты передачи проводных данных и команд по проводным сетям
  • Для контроля целостности данных при их хранении и передаче
  • Для контроля целостности кодов и прошивок
  • Для генерации уникальных кодов с хорошей энтропией*
  • И т. д. и т. п. области применения

* Пояснение: алгоритм SHA-256 обладает одним (из многих) полезным свойством — он позволяет генерировать данные (хеши) с хорошей энтропией («мерой хаоса») на основе «бедных» входных данных. Это может пригодиться для генерации уникальных паролей, создания «магических» чисел для генераторов псевдо-случайных последовательностей и т. п. применений.

На этом теоретическую подготовку можно считать законченной и далее мы перейдём к рассмотрению практических примеров работы с алгоритмом SHA-256 на микроконтроллерах.

Библиотека SHA-256

В своих экспериментах мы будем использовать библиотеку, реализующую алгоритм SHA-256 на Arduino. Эта библиотека содержит несколько примеров, которые мы подробно разберём далее.

Перед использованием функций SHA-256, библиотеку нужно установить стандартным для Arduino способом. Здесь предполагается, что вы достаточно опытный пользователь и умеете работать с Arduino IDE, создавать и загружать скетчи в микроконтроллер.

Версия Arduino IDE может быть любая, начиная с 1.6.5, микроконтроллер подойдёт тоже почти любой из линейки Arduino, например Uno, Nano, Mega и т. д.

Пример 1: получаем хеш SHA-256

Цель этой статьи — объяснить простыми словами что такое SHA-256 и дать практические навыки работы с хешированием для использования его в ваших проектах. Поэтому далее мы подробно разберём работу функций библиотеки SHA-256.

В первом примере мы просто получим хеш SHA-256 строки «abc». Вместо этой строки могла бы быть любая другая. Обратите внимание, что длина исходной строки меньше длины генерируемого хеша.

Теперь полный код первого примера:

/*
  SHA-256 Test 1
*/

#include "sha256.h"

void printHash(uint8_t* hash) {
  for (byte i = 0; i < 32; i++) {
    Serial.print("0123456789abcdef"[hash[i] >> 4]);
    Serial.print("0123456789abcdef"[hash[i]&0xf]);
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Serial.println(F("SHA-256 Test 1 start..."));
 
  Serial.println(F("Test: FIPS 180-2 B.1"));
  Serial.println(F("Expect: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"));
  Serial.print(F("Result: "));
  Sha256.init();
  Sha256.print("abc");
  printHash(Sha256.result());
}

void loop() {
  
}

И далее подробно разберём его работу. Вначале скетча мы подключаем библиотеку SHA-256

#include "sha256.h"

Далее идёт функция setup(), в которой и происходят все основные действия. Вначале мы инициализируем работу последовательного порта и выводим сообщение о старте скетча в Serial:

void setup() {
  Serial.begin(115200);
  Serial.println(F("SHA-256 Test 1 start..."));

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

  Serial.println(F("Test: FIPS 180-2 B.1"));
  Serial.println(F("Expect: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"));
  Serial.print(F("Result: "));

И затем начинаем работу собственно по вычислению хеша SHA-256, а именно, инициализируем объект Sha256.

  Sha256.init();

Далее идёт строка

Sha256.print("abc");

в которой мы направляем исходное сообщение "abc" на вход библиотечной функции вычисления хеша SHA-256. Несмотря на присутствие слова «print» в названии этой функции, она ничего не выводит на печать, а просто получает входные данные.

И наконец, функция Sha256.result() возвращает нам массив из 32-х байт, который и является хешем SHA-256 для входящей строки, в данном случае, "abc".

  printHash(Sha256.result());

Поскольку хеш (набор байтов) нужно представить в удобном для восприятия виде, то результат работы функции Sha256.result() направляется на вход функции печати хеша в Serial.

void printHash(uint8_t* hash) {
  for (byte i = 0; i < 32; i++) {
    Serial.print("0123456789abcdef"[hash[i] >> 4]);
    Serial.print("0123456789abcdef"[hash[i]&0xf]);
  }
  Serial.println();
}

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

Нам же здесь нужно знать только то, что эта функция выводит хеш в стандартном и читаемом виде.

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

Пример 2: работаем с HMAC

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

Этот вариант называется использованием HMAC (Hash-based Message Authentication Code или «основанный на хеше код проверки подлинности сообщения»). Звучит пугающе, но на самом деле всё очень просто — в процесс получения хеша добавляется секретный ключ (который знаете вы и получатель вашего сообщения) и на принимающей стороне могут удостовериться, что ваше сообщение не было изменено.

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

/*
  SHA-256 Test 2 (HMAC)
*/
 
#include "sha256.h"

uint8_t hmacKey[] = {
  0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b
};

void printHash(uint8_t* hash) {
  for (byte i = 0; i < 32; i++) {
    Serial.print("0123456789abcdef"[hash[i] >> 4]);
    Serial.print("0123456789abcdef"[hash[i]&0xf]);
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Serial.println(F("SHA-256 Test 2 (HMAC) start..."));

  Serial.println(F("Test: RFC4231 4.2"));
  Serial.println(F("Expect: b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"));
  Serial.print(F("Result: "));
  
  Sha256.initHmac(hmacKey, 20);
  Sha256.print("Hi There");
  
  printHash(Sha256.resultHmac());
}

void loop() {
  
}

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

uint8_t hmacKey[] = {
  0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b
};

который будет участвовать в получении SHA-256 хеша и который должен иметь получатель вашего сообщения. В данном случае это последовательность из 20-и байт 0x0b. Этот набор байт может быть любым, например, значения могут быть разными или длина ключа может быть меньше или больше 20-и байт. Здесь важно только то, что и вы и получатель вашего сообщения должны иметь одинаковый секретный ключ.

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

void setup() {
  Serial.begin(115200);
  Serial.println(F("SHA-256 Test 2 (HMAC) start..."));

  Serial.println(F("Test: RFC4231 4.2"));
  Serial.println(F("Expect: b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"));
  Serial.print(F("Result: "));

Затем начинается непосредственно работа по генерации хеша. Мы инициализируем объект Sha256 нашим ключом и указываем его длину 20 байт.

  Sha256.initHmac(hmacKey, 20);

Далее подаём на вход строку "Hi There", хеш которой нам нужно получить

  Sha256.print("Hi There");

Получаем результат при помощи функции

Sha256.resultHmac()

и выводим результат на печать.

printHash(Sha256.resultHmac());

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

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

Заключение

Библиотека SHA-256 содержит ещё множество примеров использования функций получения SHA-256 хешей, но все они являются вариациями вышеприведённых примеров и, если вы поняли общие принципы работы с хешированием 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 - Криптография и команды

ATSHA204A - Команда CheckMac

ATSHA204A - Команда Nonce

ATSHA204A - Команда GenDig

ATSHA204A - Команда HMAC

ATSHA204A - Команда DeriveKey

ATSHA204A - Команда DeriveKey

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

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

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

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