## Secrets v IoT
#### ako bezpečne ukladať heslá na mikrokontroléroch
[Mirek Biňas](https://bletvaska.github.io)
/ [**OpenAlt 2026**](https://openalt.cz/2026/)
[](https://bletvaska.github.io)
> The _S_ in _IoT_ Stands for Security.
notes:
- neda mi nezacat inac, ako standardnym vtipom na tuto temu a sice, ze oblast bezpecnosti v IoT je stale velmi problematicka

## Secrets
* **citlivé údaje**, ktoré **umožňujú prístup** k systémom, službám alebo dátam
* **musia byť bezpečne uložené a chránené pred neoprávneným prístupom**
* napr.: heslá, API kľúče, tokeny alebo certifikáty
[](https://owasp.org/www-project-internet-of-things/)
notes:
* OWASP je nezisková organizácia zameraná na bezpečnosť softvéru, známa hlavne rebríčkom OWASP Top 10 pre web
* jednym z jej projektov je vsak aj OWASP IoT Top 10
* zoznam 10 najvacsich hrozieb v oblasti IoT
* problem - je z roku 2018 :-(
* nic lepsie som zatial nevidel
* vy ano?
### #1 Weak, guessable or hard coded passwords
> Use of easily bruteforced, publicly available, or unchangeable credentials, including backdoors in firmware or client software that grants unauthorized access to deployed systems.

notes:
* staticka (hardcoded passwords) konfiguracia pre mqtt pripojenie
* TUYA like - posiela to na cinske servery

notes:
* to iste, len zapisane inac
* co sa stane, ak dojde k vyzradeniu loginu a hesla a admini prijmu strasne rozhodnutie, ze credentials zmenia?

notes:
* to iste, ale pre zmenu v pythone

notes:
* opat defaults pre mqtt a databazu



## Ďalšie zdroje
* tutoriály - [Random Nerd Tutorials](https://randomnerdtutorials.com/micropython-mqtt-esp32-esp8266/)
* knihy
* čo na to AI?
* a čo na to _Jan Tleskač_?
## Kde do $I&^% je problém?
### Secrets sú súčasťou kódu!
* hodnoty sú uložené v (globálnych) premenných
* nedajú sa zmeniť bez zmeny kódu
* sú čitateľné
* nie sú šifrované
1. **Izolácia**
Secrets uložíme do samostatného súboru.
2. **Šifrovanie**
Zašifrujeme uložené údaje, aby boli čitateľné iba s kľúčom.
3. **Odvodenie kľúča**
Kľúč neukladáme, ale odvodíme ho z hardvéru a inštalácie.
4. **Binárny bytecode**
Kód pracujúci so secrets preložíme do binárneho bytecode, čím sťažíme jeho analýzu.
notes:
* Preklad do .mpy (MicroPython bytecode) sťažuje analýzu zdrojového kódu, ale nie je to bezpečnostný mechanizmus porovnateľný so šifrovaním. Skôr ide o obfuskáciu alebo sťaženie reverzného inžinierstva.
### Krok 1. Izolácia
* secrets uložíme do samostatného súboru
* použijeme formát, ktorý umožňuje jednoduché čítanie aj zápis
* vystačíme si s formátom JSON
* priama podpora v jazyku MicroPython
* umožňuje vnáranie
* výhodné je použiť dataclass pre validáciu
* `/data/settings.json`
```python
import json
SETTINGS_FILE = '/data/settings.json'
# content of settings
settings = {
"ssid": "OpenAlt 2026",
"password": "Linux.Rocks!",
}
# write settings
with open(SETTINGS_FILE, 'w') as file:
json.dump(settings, file)
# read settings
with open(SETTINGS_FILE, 'r') as file:
settings = json.load(file)
```
### Dump flash pamäte
* útočník môže odpájkovať flash pamäť a prečítať ju pomocou čítačky
* dá sa to aj jednoduchšie (softvérovo)
```bash
# ak je prístupný REPL režim a viem, kde sa súbor nachádza, tak
$ mpremote cat /data/settings.json
# ak neviem, kde súbor je, prepnem zariadenie do BOOTSEL režimu
# (bez REPL: podržím tlačidlo BOOTSEL pri pripájaní USB)
$ mpremote bootloader
# ulož celý obsah flash pamäte do súboru
$ picotool save --all dump.bin
# pozri sa, čo je vo vnútri
$ strings dump.bin | grep -i password
```

notes:
* https://devcookies.medium.com/encoding-vs-hashing-vs-encryption-explained-with-examples-d92b423a4584
* este predtym ako sa posunieme ku kroku 2 mi neda jedno kratke pedagogicko-terminologicke okienko
* encoding - kódovanie
* hashing - hašovanie
* encryption - šifrovanie
### Krok 2. Šifrovanie
* obmedzenia MCU pri šifrovaní
* nízky výpočtový výkon
* malá pamäť
* obmedzená spotreba energie
* chýbajúce bezpečné úložisko kľúčov (TrustZone)
* slabá odolnosť voči fyzickým útokom
* nie vždy kvalitný generátor náhodných čísel
[`cryptolib`](https://docs.micropython.org/en/latest/library/cryptolib.html)
## AES
Advanced Encryption Standard
* **symetrická bloková šifra**
* pracuje s blokmi o dĺžke 128 bitov / 16 bytov
* podporuje kľúče s dĺžkou 128, 192 alebo 256 bitov

notes:
- https://nordlayer.com/blog/aes-encryption/
### Get Ready!
```python
# inštancia šifry AES
>>> import cryptolib
# konštruktor AES šifry potrebuje:
# kľúč (16/24/32 bytov)
>>> key = 'SuperTajnyKluc!!'
# inicializačný vektor (vždy 16 bytov)
>>> iv = '1234567890123456'
# vytvorenie objektu šifry
>>> cipher = cryptolib.aes(key, 2, iv) # mode 2 = CBC
```
### Šifrovanie textu
```python
# majme nasledujúci text
# dĺžka musí byť násobkom 16, inak treba doplniť (PKCS#7 padding)
>>> text = 'Hello, Pico 2 W!'
# zašifrovanie textu
>>> encrypted = cipher.encrypt(text)
# výsledok bude zašifrovaný
>>> print(encrypted)
b'\xa4`\xa1\xd1(w\xe3\xe2\x13\xb4\xc8$\x04\xac\xbf\xe0'
```
### Dešifrovanie textu
```python
# pre dešifrovanie potrebujeme opäť vytvoriť objekt šifry
# (zmenil sa vnútorný stav a zmenil sa iv)
>>> cipher = cryptolib.aes(key, 2, iv)
# dešifrujeme
>>> decrypted = cipher.decrypt(bytearray(encrypted))
# výsledok je ako pole bytov
>>> decrypted
b'Hello, Pico 2 W!'
# spravíme z toho reťazec
>>> decrypted.decode('utf8')
'Hello, Pico 2 W!'
```
> A cryptosystem should be secure, even if everything about the system, **except the key**, is public knowledge.
>
> -- [Auguste Kerckhoffs](https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle) (1883)

note:
- holandský lingvista a kryptograf, ktorý pôsobil vo Francúzsku
- v roku 1883, v článku La cryptographie militaire v časopise Journal des sciences militaires.
- volne prelozene: "systém nesmie vyžadovať utajenie a musí byť možné, aby padol do rúk nepriateľa bez toho, aby to spôsobilo problém."
> The enemy knows the system.
>
> -- [Claude Shannon](https://en.wikipedia.org/wiki/Claude_Shannon) (1949)

note:
- Claude Shannon ho v roku 1949 skrátil na vetu „The enemy knows the system“.
- Tej sa hovorí aj Shannonova maxima.
Kľúčový problém:
### Ako zabezpečíme kľúč?
### Bezpečnosť a RP2350
* Arm TrustZone (Cortex-M33)
* secure boot (podpísaný firmvér)
* 8 kB OTP pamäte pre kľúče
* možnosť uzamknúť debug (SWD) aj BOOTSEL
* HW generátor náhodných čísel (TRNG), SHA-256 akcelerátor
notes:
* RP2350 nema HW AES - sifrovanie bezi softverovo; encrypted boot riesi bootloader (AES-256 desifrovanie do SRAM)
* RP2350 ma aj HW ochranu proti glitchom (glitch detectors) - Raspberry Pi k tomu vypisalo aj hacking challenge
* konfiguruje sa cez Pico SDK / `picotool`, MicroPython to priamo nevystavuje
* pozor: zapis do OTP je nevratny - zle nastavenie = mrtve zariadenie
### Bezpečnosť a ESP32
* flash encryption - šifrovanie celého obsahu flash
* secure boot
* eFuse - jednorazovo zapisovateľné úložisko kľúčov
* HW akcelerátory AES, SHA, RSA, TRNG
notes:
* ESP32-S2/S3/C3 maju navyse HMAC a Digital Signature periferiu - kluc z eFuse nikdy neopusti cip, softver ho neprecita
* konfiguruje sa cez ESP-IDF (;espefuse.py;), MicroPython to priamo nevystavuje
* pozor: zapis do eFuse je nevratny - zle nastavenie = mrtve zariadenie
### A čo MicroPython?
* HW bezpečnosť využíva len **nepriamo** (akcelerácia, TRNG)
* secure boot a flash encryption sú vecou **firmvéru**, nie MicroPythonu
* bezpečné úložisko kľúčov (OTP/eFuse) z MicroPythonu **použiť nevieme**
notes:
* ESP32: cryptolib a hashlib idu cez mbedTLS, ktory pouziva HW AES/SHA; os.urandom() pouziva HW RNG
* RP2350: os.urandom() ide cez TRNG (pico_rand)
* secure boot / flash encryption = vlastny build firmveru + espefuse.py / picotool; oficialne buildy z micropython.org to nemaju zapnute
* OTP/eFuse, HMAC/DS periferie, TrustZone - bez API; jedine cez machine.mem32 = hack
* => ked neviem pouzit to, co je na doske, idealne je externe samostatne zariadenie
[](https://www.adafruit.com/product/4314)
notes:
* mozeme si pomoct napr. tak, ze k nasmu rieseniu pridame externý šifrovací koprocesor (crypto-koprocesor), ako napr ATECC608
* kľúč tak nikdy neopustí čip
* pripaja sa cez I2C, takze sa da pouzit aj z MicroPythonu
* ja ho ale nemam => kompromis: kluc odvodime zo salt + unique_id
### Krok 3. Odvodenie kľúča
* kľúč zostavíme ako kompozíciu z:
* `salt` - náhodne vygenerované pole bytov, ktoré bude viazané na inštaláciu
* `unique_id` - viazané na hardvér
* na šifrovanie/dešifrovanie vytvoríme vlastný modul `crypto.py`
### Vytvorenie salt
```python
import os
SALT_FILE = '/data/.salt'
def create_salt(salt_path: str = SALT_FILE) -> None:
with open(salt_path, 'wb') as file:
file.write(os.urandom(16))
```
### Vytvorenie kľúča
```python
import machine
import hashlib
def create_key(salt_path: str = SALT_FILE) -> bytes:
with open(salt_path, 'rb') as file:
salt = file.read()
hasher = hashlib.sha256()
# adding data to hash
hasher.update(machine.unique_id())
hasher.update(salt)
# return hash
return hasher.digest()[:16] # AES-128
```
### Funkcia na šifrovanie
```python
import cryptolib
import binascii
def encrypt(plaintext: str, salt_path: str = SALT_FILE) -> str | None:
if plaintext is None:
return None
key = create_key(salt_path)
iv = os.urandom(16)
data = plaintext.encode('utf-8')
pad = 16 - (len(data) % 16)
data = bytearray(data + bytes([pad] * pad))
cipher = cryptolib.aes(key, 2, iv)
encrypted = cipher.encrypt(data)
return binascii.hexlify(
bytearray(iv) + bytearray(encrypted)
).decode('ascii')
```
### Funkcia na dešifrovanie
```python
def decrypt(ciphertext: str, salt_path: str = SALT_FILE) -> str | None:
if ciphertext is None:
return None
key = create_key(salt_path)
raw = binascii.unhexlify(ciphertext)
iv = raw[:16]
data = bytearray(raw[16:])
cipher = cryptolib.aes(key, 2, iv)
decrypted = cipher.decrypt(data)
pad = decrypted[-1]
return bytes(decrypted[:-pad]).decode('utf-8')
```
```python
import json
from crypto import encrypt, decrypt
SETTINGS_FILE = '/data/settings.json'
# content of settings
settings = {
"ssid": "OpenAlt 2026",
"password": encrypt("Linux.Rocks!"),
}
# write settings
with open(SETTINGS_FILE, 'w') as file:
json.dump(settings, file)
# read settings and decrypt password
with open(SETTINGS_FILE, 'r') as file:
settings = json.load(file)
print(decrypt(settings['password']))
```
### Krok 4. Binárny bytecode
* modul `crypto.py` preložíme do binárneho bytecode (`crypto.mpy`)
* na zariadenie nahráme iba `crypto.mpy`, nie zdrojový kód
* sťaží to analýzu, ale nie je to šifrovanie (iba obfuskácia)
## [mpy-cross](https://gitlab.com/alelec/mpy_cross )
the micropython cross compiler utility
```bash
# najprv nainštalujeme
(venv) $ pip install mpy-cross
# overíme verziu:
# (verzia .mpy musí sedieť s firmvérom na zariadení)
(venv) $ mpy-cross --version
MicroPython v1.29.0 on 2026-08-29; mpy-cross emitting mpy v6.3
# preložíme modul crypto.py
(venv) $ mpy-cross crypto.py
# crypto.mpy presunieme do /src/lib
# crypto.py presunieme do /stubs/
```
### Zhrnutie
* **secrets nepatria do kódu** - ani do repozitára
* izolácia + šifrovanie + kľúč viazaný na HW + `.mpy` = **sťaženie útoku**
* bez HW podpory je to stále len **obfuskácia** - útočník s fyzickým prístupom vyhrá
* skutočná ochrana = **HW**: secure boot, flash encryption, OTP/eFuse, krypto-koprocesor
* **nedokonalé zabezpečenie je stále lepšie ako žiadne**
notes:
* zopakovat 4 kroky: izolacia, sifrovanie, odvodenie kluca, bytecode
* uprimne: Kerckhoffs - bezpecnost ma stat na kluci; nas kluc je ale odvodeny z udajov, ktore su na zariadeni
* cielom je, aby skopirovany settings.json bez zariadenia nebol na nic a aby heslo nebolo vidiet cez strings
* ak to myslim vazne: RP2350/ESP32 s HW zabezpecenim alebo ATECC608
## Odkazy
* [`cryptolib`](https://docs.micropython.org/en/latest/library/cryptolib.html) - cryptographic ciphers
* [MicroPython `.mpy` files](https://docs.micropython.org/en/latest/reference/mpyfiles.html)
* [`alelec/mpy_cross`](https://gitlab.com/alelec/mpy_cross) - MicroPython `mpy-cross` distribution

(**https://bit.ly/4epMP7z**)