Ako sme si poukázali na skutočnosť v jednom z predchádzajúcich článkov, knižnica Arduino_LoRa, ktorá sa používa na implementáciu LoRa PPP komunikácie s modulmi na báze čipsetov Semtech SX127X nepodporuje šifrovaný prenos dát. Znamená to, že ktokoľvek, kto si vhodne nastaví prijímač (zhodná frekvencia, šírka pásma, coding rate) uvidí, čo prenášate v čitateľnej podobe (plaintexte). To nemusí byť pre niektoré aplikácie žiadúce.
Keďže kanál šifrovaný nie je, môžeme ísť cestou šifrovania obsahu správ. Teda ktokoľvek s vhodne nastaveným prijímačom dokáže získať iba zašifrované dáta (zašifrovaný text), ktoré v rozumom čase bez dešifrovacieho kľúča nebude vedieť rozlúštiť a dostať ich do čitateľnej podoby (plaintextu). Ukážeme si najľahšie spôsoby na implementáciu, ale aj štandardné šifrovacie algoritmy, ktoré budeme implementovať na mikrokontroléri ESP32 aj s využitím jeho vstavaných kryptografických akcelerátorov a programovej implementácie knižnice mbedTLS (PolarSSL) priamo v Arduino Core, resp. ESP-IDF frameworku. Všetky šifry, ktoré si ukážeme budú symetrické a ich výsledkom budú šifrované dáta v rovnakej dĺžke ako pôvodné dáta. Symetrická šifra využíva rovnaký kľúč na proces šifrovania, aj dešifrovania.

Budeme využívať dáta, konkrétne štruktúru, ktorá bude mať dve premenné, konkrétne dátové typy int (4 bajty) a float (4 bajty), teda celkom 8 bajtov dát. Pre int hodnotu budeme používať fixnú hodnotu 1234 a pre float 3.14159. Toto budú zároveň dáta, ktoré budeme chcieť po zašifrovaní získať dešifrovaním opäť do čitateľnej podoby v rovnakej hodnote. Poznámka: Ale v dokumentácii Arduino.cc vidím, že dátový typ int má 2 bajty! Áno, ale to je prípad Arduina Uno, či obdobného Arduina, čo je 8-bitový MCU a nie 32-bitový ako je ESP32.
Najľahším spôsobom pre pozmenenie dát je použitie bitovej operácie, čo zaručí, že dáta vieme následne získať opäť aj v čitateľnej podobe. Napríklad XOR, t.j. exkluzívny súčet. Výsledkom tejto bitovej operácie je 1, ak sú bity rozdielne a 0 ak sú bity rovnaké. XOR sa realizuje medzi bitom hodnoty a bitom kľúča. Zvolil som kľúč fixnej hodnoty, ktorá odpovedá veľkosti štruktúry, t.j. 8 bajtov. Operácia XOR sa bude teda realizovať medzi vstupom (dátami, nazývame aj otvorený text) a šifrovacím kľúčom. XOR operácia v takejto implementácii je najjednoduchšia symetrická šifra, nakoľko pri dešifrovaní dát sa znova použije XOR s rovnakým kľúčom pre získanie pôvodných, čitateľných dát. Bežne má ale XOR zastúpenie ako súčasť komplexného šifrovacieho algoritmu s násobnou zložitosťou.
Encrypted = D XOR K
Decrypted = Encrypted XOR K = (D XOR K) XOR K = D

Najľahšia implementácia šifry s využitím bitovej operácie XOR
Je XOR bezpečný pre takúto aplikáciu? Ak používame identický kľúč dookola, nie je vôbec bezpečný. Keďže je táto operácia "lineárna", potenciálny útočník môže v dátach vidieť tzv. vzory a odvodiť si opakovanie dát. Keď sa využije náhodný kľúč zakaždým pri prenose dát, takáto šifra je technicky vzaté bezpečná. Vyššia bezpečnosť by sa dosahovala v prípade, že by sme mali vstup s variabilnou dĺžkou a k tomu by sa dynamicky prispôsobil aj symetrický kľúč. Teda by dynamicky menil aj počet možností. Nakoľko máme fixný 8 bajtový vstup, teda využívame 8 bajtový kľúč.
Ak by sa kľúč zakaždým menil, bolo by nutné hodnotu tohto kľúča prenášať LoRa vysielačom k prijímaču, aby ten dokázal dáta dešifrovať. Okrem hľadania spôsobu ako kľúč prenášať bezpečne by bolo otázne aj navýšenie spotreby, keďže kľúč predstavuje v tomto prípade 8 bajtov navyše, ktoré sa musia preniesť skrz LoRu, čo zvyšuje aj celkový airtime zariadenia.)
AES
Poďme sa pozrieť na komplexnejšie šifrovacie algoritmy, ktoré ponúka priamo knižnica mbedTLS v Arduino Core, respektíve ESP-IDF frameworku pre ESP32. Primárne ide o blokovú šifru Advanced Encryption Standard (AES) s rôznymi režimami prevádzky. AES má u mikrokontroléra ESP32 veľké zastúpenie a to aj na hardvérovej úrovni. Nejde len o kryprografický akcelerátor pre AES, ale aj o dalšie interné funkcionality mikrokontroléra, ktoré AES využívajú ako Flash Encryption, validácia podpísaného firmvéru (s týmito funkcionalitami sa stretnete iba v ESP-IDF, nakoľko Arduino Core také možnosti nemá a pracuje na vyššej vrstve OSI/ISO modelu). AES má zastúpenie aj v programovej implementácii pre šifrovaný prenos, napr. pre protokoly HTTPS, MQTTS a podobne. Pre všetky ukazované režimy AES využívam rovnaký 128-bitový kľúč, ktorý môže mať ešte aj veľkosti 192, alebo 256 bitov. Vyššia hodnota znamená vyššiu bezpečnosť, keďže sa exponenciálne navyšuje počet možností.
AES-ECB
Najľahším režimom na implementáciu je AES-ECB (Electronic Codebook). Tento režim prevádzky sa vyznačuje tým, že spracuváva bloky dát o velkosti 16 bajtov. Ako vidíme, náš dátový vstup má v tomto prípade 8 bajtov. To znamená, že potrebujeme pridať tzv. padding a dorovnať túto veľkosť dát na 16 bajtový blok. Ak by sme mali napríklad 32 bajtový vstup, rozdelí sa na dva bloky po 16 bajtov.
Samotné dorovnanie (padding) môžeme urobiť vlastnou implementáciou, napríklad doplnenie nulami, alebo môžeme použiť aj metódu PKCS#7. Samotné šifrovanie nie je iba jedna operácia (v porovnaní so XOR), ale ide o komplexnú operáciu, ku ktorej je inverznou funkciou dešifrovanie. AES-ECB sa radí k najmenej bezpečnému režimu, nakoľko neprináša náhodnosť a rovnaké bloky majú stále rovnaký výstup šifrovaných dát pri využití rovnakého kľúča. Obdobne ako pri opisovaní XOR operácie pre implementáciu vlastnej "šifry" aj tu dochádza k tomu, že účtočník môže vidieť vzory. Zmenou symetrického kľúča by sa dosiahlo to, že medzi rôznymi správami nebude možné vidieť identický vzor, čo ju robí bezpečnejšou. Aj tu je ale dilema nad tým, akým spôsobom kľúč opakovane z vysielača na prijímač prenášať.
Encrypted = AES_Encrypt(D, K)
Decrypted = AES_Decrypt(Encrypted, K) = AES_Decrypt(AES_Encrypt(D, K), K) = D

AES-ECB režim, rovnakému vstupu vždy odpovedá rovnaký výstup pri rovnakom kľúči
AES-CBC
AES ponúka aj režimy s vyššou bezpečnosťou vďaka implementácii náhodnosti. Následujúcim režimom je AES-CBC, ktorý zavádza zreťazenie blokov a spracováva rovnako ako AES-ECB po 16-bajtových blokoch, teda opäť vyžaduje dorovnanie vstupu. Dáta prvého bloku sú XOR-ované s inicializačným vektorom (IV). Každý nasledujúci blok vstupu je potom XOR-ovaný s výstupom predchádzajúceho šifrovaného bloku. Toto zaručuje, že aj pri použití identického symetrického kľúča budú výsledné šifrované dáta zakaždým iné, za predpokladu, že sa zmení IV.
V tomto prípade šifrovaný text nemá viditeľné vzory, nakoľko aj pri identickom vstupe a identickom symetrickom kľúči bude šifrovaný text stále iný. Inicializačný vektor aj v tomto prípade musí byť prenášaný medzi vysielačom a prijímačom. Dobrou správou je, že tento vektor môžete prenášať nešifrovane a to, že ho bude poznať útočník nie je problém. Obe LoRa zariadenia majú v konfigurácii uložený symetrický kľúč, ktorý nezdieľajú a ani si ho neposielajú. Inicializačný vektor musí byť ale náhodný pri každej správe, ak by bol stále rovnaký, tak to samozrejme urobí viditeľné vzory.
Takto zvolený šifrovací režim môžeme hodnotiť ako bezpečný s náhodným vektorom pri každej správe. Dokonalejšou verziou CBC je režim CFB, ktorý pozmeňuje poradie operácii. Najprv blok zaširuje a jeho výsledkom XOR-uje následujúci blok dát. Nakoľko v našom prípade máme iba jeden blok, vykoná sa iba XOR pre prvý blok s inicializačným vektorom. Prakticky sa šifrovaný výstup rovná prípadu ako pri AES-ECB z pohľadu bezpečnostného rizika.
Encrypted₁ = AES_Encrypt(D₁ XOR IV, K)
Encrypted₂ = AES_Encrypt(D₂ XOR Encrypted₁, K)
...
Decrypted₁ = AES_Decrypt(Encrypted₁, K) XOR IV = (AES_Decrypt(AES_Encrypt(D₁ XOR IV, K), K)) XOR IV = D₁ XOR IV XOR IV = D₁
Decrypted₂ = AES_Decrypt(Encrypted₂, K) XOR Encrypted₁ = (AES_Decrypt(AES_Encrypt(D₂ XOR Encrypted₁, K), K)) XOR Encrypted₁ = D₂ XOR Encrypted₁ XOR Encrypted₁ = D₂
...


Pozmenený inicializačný vektor vytvára úplne iný výstup aj pre identický vstup u AES-CBC s identickým kľúčom
AES-CTR a AES-GCM
Medzi vyššie režimy algoritmu AES môžeme zariadiť CTR (Counter) a GCM (Galois/Counter Mode), ktoré sú už znateľne zložitejšie. Ide už o prúdové šifry, teda spracovanie je po bajtoch. Ak máte 5 bajtov vstupu, výsledkom je 5 bajtov šifrovaných dát. Nie je potrebné dorovnanie vstupu na 16 bajtové bloky ako v predchádzajúcich prípadoch.
Režim AES-CTR využíva počítadlo, ktoré sa pre každý blok inkrementuje. AES potom nešifruje priamo dáta, ale najprv zašifruje aktuálnu hodnotu počítadla a výsledok sa XOR-uje so vstupom. Výhodou tohto prístupu je, že šifrovanie aj dešifrovanie sú identické operácie (iba XOR), čo umožňuje efektívnu paralelizáciu.
AES-GCM rozširuje princíp CTR o autentifikáciu dát. Popri samotnom šifrovaní poskytuje aj overenie integrity a autenticity správy. Je preto veľmi populárny v moderných bezpečnostných protokoloch (napr. TLS 1.2/1.3, IPsec). Výhodou AES-GCM je, že kombinuje rýchle šifrovanie, možnosť paralelizácie, elimináciu paddingu a zabezpečenie integrity. Na druhej strane, jeho správna implementácia je zložitejšia, najmä kvôli požiadavke na unikátny a nikdy neopakujúci sa nonce (počiatočný čítač).
GCM vracia nielen šifrované dáta, ale aj autentifikačný tag, ktorý slúži na overenie integrity a autenticity prijatých dát. Tento tag sa musí preniesť spolu s dátami a overiť na strane príjemcu. Ak overenie zlyhá, dešifrovanie by sa nemalo vykonať – ide o ochranu pred manipuláciou alebo podvrhnutím správy.

Pre AES-GCM je viditeľná vyššia výpočtová náročnosť, viac ako 20-násobná oproti AES-CBC
Ktorý režim AES šifry zvoliť?
To závisí od aplikácie. Pre opisovanú LoRa aplikáciu je priorita mať nízky vysielací čas a nízku spotrebu. Je preto nutné nájsť niečo bezpečné, ale zároveň efektívne aj z pohľadu prevádzky a vyvážiť to. Nemusíme pre primitívne dáta o napätí používať najbezpečnejšiu šifru na svete. Takéto dáta sú posielané raz za 10 minút a teda, či by útočník prelamoval heslo 5 alebo 10 rokov je jedno, nakoľko z nich nezíska žiadny profit a v praxi by sa prelomením takejto malej IoT aplikácie nikto nezaoberal. Rovnako tak je výhodou ak útočník ani nevie, aký algoritmus či režim používate. Kľúč v žiadnom prípade neprezraďte a ani štruktúru dát. Pre útočníka môže byť potenciálnym vodítkom aj to, ako dĺžku má výstup. Ak je to násobok 16-tich bajtov, tak je jasné, že ide o blokovú šifru, čo môže vylúčiť GCM, CTR režimy a podobne.
Samotné šifrovanie nezaberie veľa času vďaka kryprografickému akcelerátoru AES, ktorým je mikrokontróler ESP32 vybavený. Vyššie nároky budú na výsledný airtime (vysielací čas), ak bude potrebné pri každej správe prenášať aj kľúč. Teda úmerne sa navýši aj spotreba, čo nemusí byť ideálne pre potrebný lifetime batérie. AES-CBC vyzerá veľmi zaujímalo.
Celkovo sa ale musí prenášať 32 bajtov payloadu miesto 8 bajtov. Je to spôsobené jednak zarovnaním 8 bajtov na 16 bajtov, čo predstavuje blok dát, ktorý je spracovávaný a tiež 16 ďalších bajtov je použitých na inicializačný vektor, ktorý je nutné prenášať z dôvodu náhodnosti šifrovaného textu, aby aj pre identické dáta boli šifrované dáta náhodné a nevyskytoval sa tam vzor tak, ako u AES-ECB. Celkový vysielací čas nebude úmerný štvornásobku, bude to o niečo menej z dôvodu, že LoRa správa, resp. jeho paket obsahuje aj hlavičku, ktorá bude stále tá istá a sama o sebe má určitú veľkosť.
Z pohľadu efektivity a bezpečnosti by som využil minimálne AES-CBC s prenášaným inicializačným vektorom pri každej správe. Režimy ako AES-GCM sú už veľmi výpočtovo náročné a z pohľadu takéhoto výkonového zaťaženia pri každej správe, kde sa výpočet predĺži 20-násobne + sa musí prenášať aj tag násobne zníži operačný čas zariadenia, čo by pre takúto kritickú LoRa aplikáciu nebolo postačujúce.
Sú v Arduino Core, resp. ESP-IDF aj iné algoritmy v mbedTLS?
Existujú tu alternatívne šifrovacie algoritmy ako napríklad ARIA, či CAMELLIA, ktoré sú považované za bezpečné a majú pôvod v Ázii. Sú dostupné v obdobných režimoch ako AES. V mbedTLS pre ESP32 nájdeme aj staršie šifrovacie algoritmy, ktoré ale v dnešnej dobe už nie sú tak bezpečné a spomenuté šifrovacie algoritmy prišli ako ich nástupcovia. Ako môžeme vidieť na obrázku nižšie, Camellia-CBC a ARIA-CBC má násobne vyšší čas v porovnaní s AES-CBC pri zachovaní rovnakých podmienok (dĺžka vstupu, dĺžky kľúča...). Je to spôsobené tým, že u AES sa používa vstavaný kryptografický akcelerátor v ESP32. Pre Camelliu a ARIU nemá ESP32 kryptografický akcelerátor.

Camellia-CBC

ARIA-CBC
K starším algoritmom, ktoré sú dostupné v mbedTLS môžeme zaradiť DES, 3DES, ChaCha. Dosiahli sme šifrovanie payloadu a výsledkom bude najmä to, že každý prijímač v okolí príjme šifrovaný text miesto originálneho textu. Napríklad ½ñýП5ìp¨D!1æ¤ü a po dešifrovaní získa 1234 3.141590. Prijímače, ktoré nepoznajú použitú šifru, algoritmus nebudú schopné získať dáta v plaintexte. A prakticky to, čo príjmu môžete pre ich obsluhu, ktorá vidí log vyzerať ako "rozsypaný čaj", čo sa dá niekedy vyhodnotiť aj len ako náhodný šum, či len čiastočné zachytenie signálu napr. s iným coding rate, s čiastočnou šírkou pásma a podobne.