Dnes si ukážeme aj prakticky so zdrojovým kódom, ako implementovať šifrovanie payloadu v LoRa PPP (Point-to-Point) aplikácii. Hardvérovo budeme využívať mikrokontróler ESP32 v najbežnejšej WROOM-32 verzii a pre prenos samotný využijeme RA-02 modul od AI-Thinker na báze chipsetu Semtech SX1278 (433 MHz). Z pohľadu softvéru použijeme Arduino Core 3.2.0 pre ESP32 a tiež knižnicu Arduino LoRa pre samotnú LoRa komunikáciu.
Ako sme si spomenuli v predchádzajúcom článku, ESP32 má vstavaný kryptografický AES akcelerátor, ktorý významne urýchľuje proces šifrovania, či dešifrovania. ESP32 má v v Arduino Core, i ESP-IDF (Espressif IoT Development Framework) kryptografickú knižnicu mbedTLS (PolarSSL), ktorú budeme používať. Knižnica má implementované rôzne šifrovacie algoritmy a má API s funkciami, ktoré umožňujú vykonávanie kryptografických operácii s konkrétnou šifrou, či už ide o šifrovanie, dešifrovanie a podobne.

Využijeme blokovú šifru AES v režime CBC (AES-CBC). O samotnom režime si môžete detailnejšie prečítať na Wikipédii, ak vás to zaujíma. Využijeme najdlhší možný symetrický kľúč o veľkosti 256 bitov. V našom prípade má hodnotu 1010001101111111000110010100110110000010111001100011101111000001010110001001001001101000000011101111001111010100101101110101110000011010100011010011001100001001011110111110111001000000110110100010011001100100101111100001000101110101100100001100101100101111, čo je nižšie reprezentované aj v hexadecimálnej reprezentácii, ktorá je prehľadnejšia.

Bloková schéma procesu šifrovania v AES-CBC

Bloková schéma procesu dešifrovania v AES-CBC
Takýto kľúč je bezpečný a označuje sa aj ako kvantovo rezistentný, keďže možností pre kľúč, ktoré môže nadobudnúť je 2ˆ256. Bruteforce útok pre získanie tohto kľúča (teda s overením všetkých možností) by trvalo tisíce rokov aj s najvýkonnejšími počítačmi sveta.

256-bitový AES kľúč pre šifrovanie a dešifrovanie zdieľaný medzi vysielačom a prijímačom
Keďže AES-CBC spacuváva bloky o veľkosti 16 bajtov, častokrát sa vyžaduje zarovnanie na 16 bajtov, ak blok obsahuje menej bajtov. Toto zarovnanie sa môže realizovať vlastnou metódou, napríklad doplnením núl alebo podľa štandardu PKCS#7, ktorý budeme používať. Tento štandard vykonáva zarovnanie spôsobom, že dosadí chýbajúce bajty, ktorým nastaví hodnotu, ktorej odpovedá počet dosadených bajtov. Teda ak máme napríklad 7 bajtov, doplní 9 bajtov s nastavenou hodnotou 09 (HEX) a získame blok s celkovou veľkosťou 16 bajtov v ktorom bude táto reprezentácia použitá.
V predchádzajúcom článku sme si ukazovali len 8 bajtový payload, ktorý je zarovnaný na 16 bajtový blok, ktorý bloková šifra spracováva. Chceme si ale ukázať plný potenciál AES v režime CBC, čo je reťazenie blokov, skrz funkciu XOR, ktorá sa realizuje medzi šifrovaným textom predchádzajúceho bloku a otvoreným textom bloku, ktorý bude šifrovaný. Musíme mať preto minimálne dátové bloky dva a preto musíme zabezpečiť, aby sme mali payload minimálne 17 bajtov, ktorý bude zarovnaný na 32 bajtov, čo predstavuje dva bloky otvoreného textu (vstupných dát).
Najjednoduchšie by bolo použiť reťazec znakov, ale aby sme mali zmysluplné dáta, použijeme 2x premennú typu double a jednu premennú typu byte, čo nám dá v súčte požadovaných 17 bajtov štruktúry, avšak reálne nebudeme mať 17 bajtov, ale 24. To je spôsobené tým, ako je v pamäti štruktúra mapovaná a je automaticky zaokrúhlená na 8 bajtov, teda v pamäti má 24 bajtov, i keď má len prvky s celkovou veľkosťou dátových typov 17 bajtov. Do premenných dáme GPS súradnice, ktoré predstavuju pozíciu námestia v meste Poprad a 1-bajtovú premennú nastavíme na najvyššiu možnú hodnotu a to 255. Tieto dáta budú statické a nebudú sa meniť, aby som poukázal na fakt, že aj identické vstupné dáta budú produktovať vždy iný šifrovaný text z dôvodu zmeny inicializačného vektora a reťazenia blokov, pričom daný prvý blok je ovplyvnený už inicializačným vektorom, čo kaskádovo robí zmeny aj v ďalšom šifrovanom texte.
Pre identické vstupné dáta tak môžeme získať náhodný výsledný šifrovaný text, napríklad:
80 77 41 A7 ED 68 87 95 C3 0D F3 39 76 95 51 F9 5E 5E 3E 5F EF DC 4A CC 04 60 2E 62 56 DE A9 A9
D8 A5 08 CD 0B 41 11 FB 2F DE F1 6B B3 0C 64 8D 21 97 CF 11 2D 35 21 28 57 5D E5 05 C8 96 CC 7C
BC 01 85 53 59 EA FA A8 D2 9C F3 D0 97 B5 AC F8 5C 63 C9 E8 87 48 CB B1 06 30 4E 1C 51 43 A3 84
a iné...

17-bajtová štruktúra, ktorá bude mapovaná v pamäti ako 24 bajtová a následne dorovnaná na 32 bajtov
AES-CBC využíva pre prvý blok inicializačný vektor, s ktorým sa XOR-uje ešte pred šifrovaním. Preto je nutné zabezpečiť aby sa inicializačný vektor menil a najmä, aby bol náhodný. Mnoho generátorov využíva napríklad lineárnu operáciu, aby dostal náhodne číslo, avšak táto náhodnosť je závislá a ide tak o pseudo-random generátor.
True random generátor má väčšiu entropiu a náhodnosť založenú napríklad aj na teplote a mnohých iných parametroch. Vplyv na náhodnosť môže mať aj spustenie WiFi, Bluetooth a podobne. Náhodnosť u ESP32 je možná, nakoľko má hardvérový generátor náhodných čísel (RNG). Neodporúča sa inicializačný vektor inkrementovať, alebo vykonávať s ním akúkoľvek lineárnu operáciu. Tento vektor sa môže prenášať aj nešifrovane, čo využijeme v tomto prípade pre prenos medzi vysielačom a prijímačom. V opačnom prípade by pri zmene iniciazačného vektora nedokázal prijímač dešifrovať správu a získať pôvodný otvorený text (dáta).
Z pohľadu softvéru budú mať oba zariadenia identický AES kľúč o veľkosti 256 bitov uložený priamo vo svojom firmvéri, ktorý nie je viditeľný pre potenciálneho útočníka. Rovnako tak budú zdieľať aj identickú LoRa konfiguráciu a okrem časti paketu so šifrovanými dátami bude vysielač vysielať aj aktuálne inicializačný vektor. Tento vektor o veľkosti 16 bajtov bude pripojený pred šifrovaný text, teda vysielač vyextrahuje prvých 16 bajtov payloadu a získa z neho inicializačný vektor v plaintexte a použije ho v procese dešifrovania. Ďalších 32 bajtov predstavuje šifrovaný text, skrz unpadding sa odstránia prebytočné bajty, ktoré nepredstavujú premennné a ich hodnoty. To sa vykoná dynamicky na základe známej veľkosti štruktúry, ktorú prijímač očakáva.
Rovnako tak som použil aj rozdielne architektúry mikrokontrolérov ESP32, aby som demonštroval kompatibilitu medzi platformami, ktorá nemá vplyv na použitie identického firmvéru v časti šifrovania a dešifrovania s knižnicou mbedTLS a jej API. Na vysielač som použil Lolin32 s procesorom ESP32-WROOM-32, ktorý je najpoužívanejší a najdostupnejší. Na strane prijímača som použil ESP32-C6 XIAO od Seeed Studio. Programovo sa implementácia pre SPI zbernicu a LoRu u ESP32-C6 líši, nakoľko som využil FSPI (Flexible Serial Peripheral Interface).
Vysielač - ESP32-WROOM-32 (Lolin32), využíva aj deep sleep ULP:

DPS pre XIAO_LoRa (prijímač) a 433_LoRa_Lolin32 (vysielač)
Prijímač - ESP32-C6:

Šifrovaný text môžeme skúsiť vypísať aj vo forme ASCII znakov, ale mnoho z nich je nečitateľných a aj samotný Serial monitor ako súčasť Arduino IDE nedokáže tieto znaky vypísať, respektíve ich nahrádza predvolenými znakmi, napríklad bodkou. Pre vizualizáciu šifrovaného textu je použitie HEX reprezentácie asi najrozumnejšie aj z hľadiska prehľadnosti. Ako sme si ukázali v predchádzajúcom článku, samotné šifrovanie a dešifrovanie trvá len niekoľko mikrosekúnd vďaka kryprografickým akcelerátorom AES u oboch testovaných hardvérových platforiem.

Na ukážku aj vypísané dešifrované dáta s paddingom štandardu PKCS#7, kde je viditeľné, že bolo skutočne doplnených len 8 bajtov a nie 15, keďže majú hex hodnotu 08, nakoľko je 17 bajtová štruktúra v pamäti mapovaná na 24 bajtov kvôli zaokrúhleniu na 8 bajtov, čo je dané architektúrou a tým, ako je pamäť čítaná. Reťazec nadobúda hodnoty: 87 36 00 1B 10 87 48 40 95 27 10 76 8A 4D 34 40 FF A5 A5 A5 A5 A5 A5 A5 08 08 08 08 08 08 08 08.

Dosiahli sme tak úspešné šifrovanie a dešifrovanie dát s náhodným inicializačným vektorom, ktorý generuje vysielacie ESP32 priamo svojim hardvérovým RNG pre AES-CBC režim. Treba ale dodať, že táto metóda chráni naše dáta pred prečítaním treťou osobou. Tento režim nerieši autenticitu, či integritu dát. Tento algoritmus nedokáže odhaliť, ani zaručiť, že prijaté dáta na prijímači pochádzajú iba od konkrétneho vysielača, alebo že neboli po ceste pozmenené (man in the middle), alebo že ide dáva od úplne iného vysielača. Potenciálny útočník tak môže zasielať aj správy s variabilnou dĺžkou, ktoré v tomto prípade prijímač príjme a dokonca aj spracuje. Keďže prijímač nič nevysiela, útočník bez prístupu k jeho Serial rozhraniu nebude vedieť ani to, ako dešifroval jeho podvrhnutý šifrovaný text, respektíve paket.
V našom prípade je na prijímači len validácia, ktorá sa zaoberá dĺžkou prijatého payloadu, kde overuje či je payload dlhší ako veľkosť inicializačného vektora. Ak áno, následne dešifruje správu a hodnoty na základe veľkosti priraďuje ku konkrétnym členom štruktúry. Celkom sa tak priradí 17 bajtov, teda ak príjmeme správu, ktorá bude mať povedzme 200 bajtov, tie bajty navyše sa skrátka odignorujú a nevykoná sa s nimi žiadna operácia. Keďže útočník nepozná šifrovací AES kľúč, nedokáže nám podvrhnúť šifrované dáta, aby sme ich získali vo výsledku v plaintexte, ktorý by on chcel.
Ak hodnota po dešifrovaní nebude čitateľná, skrátka to skonči hodnotou 0, v horšom prípade môže dôjsť k reštartovaniu mikrokontroléra z dôvodu chyby, ak by došlo k nevykonateľnej inštrukcii, či nesúladu dátových typov, napríklad text miesto číselnej hodnoty. Nemyslím si, že by útočník dokázal zaslať škodlivý spustiteľný kód, nakoľko by sa z pohľadu dátových typov nevykonal a útočník by aj tak nevedel AES kľúč, ktorým je payload šifrovaný. Rovnako tak je limitovaná aj veľkosť payloadu knižnicou, respektíve aj hardvérovo Semtech chipsetom.

Rozdelenie LoRa paketu, ktorý tvorí inicializačný paket a šifrovaný text (dáta)
V našom prípade tak payload LoRa správy obsahuje 48 bajtov, nakoľko je tvorený inicializačným vektorom (16 bajtov) a šifrovaným textom o veľkosti 32 bajtov. Vektor sa musí zasielať pri každej správe pre bezpečnosť algoritmu a náhodnosť. Takto šifrované dáta nedokáže útočník dešifrovať ani v priebehu tisícov rokov, aj keby mal najvýkonnejší počítač na svete vrátane kvantových. Možností je až tak veľa a tento bruteforce útok by musel testovať len s jedným inicializačným vektorom pre konkrétny šifrovaný text, ktorému prislúcha.
Aktuálne programové implementácie budú fungovať aj na iných mikrokontrolérov z rady ESP32. Knižnica mbedTLS, ktorá sa používa je viazaná priamo na tieto architektúry a tak zdrojový kód nebude kompatibilný s inými mikrokontrolérmi ako STM32, RP2040, MG24. V takomto prípade treba siahnuť po kryptografickej knižnici priamo pre dané mikrokontroléry a vykonať úpravy pre zabezpečenie kompatibility.
Programovú implementáciu pre mikrokontroléry ESP32, ESP32-C6 nájdete na Githube projektu, kde môžete vyskúšať šifrovanie skrz AES-CBC:
https://github.com/martinius96/AES-CBC-LoRa-ESP32
Rovnako tak je možné prenášať aj dáta, ktoré sa menia v každej správe, čo môžeme demonštrovať v krátkom videu. Skrz generátor náhodných čísel u ESP32 môžeme generovať napríklad náhodné GPS súradnice a rovnako tak aj 1 bajtovú hodnotu pred odoslaním každej správy.
Pri generovaní dbáme nato, aby sme dosiahli požadovaný počet desatinných miest u dátových typov typu double a u byte využívame bitovú AND operáciu (&) s hodnotou 0xFF, ktorá zabezpečí, že z generovaného čísla vyextrahujeme len tzv. spodný bajt 32-bitového čísla, teda hodnotu od 0 do 255 (DEC). Obdobne by šlo použiť trebars aj modulo, ale táto bitová operácia je rýchlejšia. Môžeme tak vidieť na prijímači variáciu rôznych prijatých dát, ktoré boli získané dešifrovaním šifrovaného textu, ktorý obdržal od vysielača. Payload má stále identickú veľkosť, aj keď sa mení, rovnakú ako pri statických dátach rovnakej štruktúry.