Ce que peut faire un attaquant qui connaît le code du micrologiciel mais n’a pas accès aux secrets de l’appareil.
Le micrologiciel est public : les formats de paquets, les longueurs et les algorithmes (X25519, HMAC-SHA256, HKDF) sont connus. Le BLE est un air libre : les annonces et les écritures GATT peuvent être écoutées. Ni dans le micrologiciel ni dans les ondes ne figure le moindre secret.
Où vivent les clés :
owner_secret (32 octets) — uniquement dans la NVS du contrôleur et sur le téléphone du propriétaire. Elle est dérivée par ECDH X25519 lors de l’appairage et ne circule jamais en clair.guest_key — sur le téléphone de l’invité. Elle arrive du serveur dans un lot via un canal WSS sécurisé, jamais par BLE.| Attack | Defence |
|---|---|
| Falsifier un FIRE | HMAC-SHA256. Sans owner_secret ni guest_key, c’est un 2128 de force brute — irréalisable. |
| Falsifier une écriture TIME | Le même HMAC sur bleId + epochMs. |
| Rejouer une annonce interceptée | Le compteur est monotone : en cas de répétition ou de diminution, le HMAC ne correspond pas. |
| Rejouer un fire intercepté | L’anneau de nonces invalide ceux déjà utilisés. |
| Reculer l’horloge du contrôleur | Le cliquet : tout TIME < cliquet est rejeté. Le cliquet réside en NVS et survit à une coupure de courant. |
| Utiliser un lot expiré | Après le fonctionnement du contrôleur, le cliquet vaut au moins la dernière heure réelle. Pour un lot expiré, validUntil est sous le cliquet : le fire échoue sur la durée de vie. |
| Avancer l’horloge d’un an et exclure tous les invités | Un plafond de +24 h par écriture TIME d’invité. Le propriétaire n’a pas de plafond : on lui fait confiance. |
| S’appairer comme second propriétaire par-dessus le premier | Après l’appairage, le contrôleur garde owner_secret en NVS et ne repasse jamais en mode appairage sans réinitialisation d’usine. |
Requires: un lot invité valide et non expiré.
Action: à chaque passage à portée, écrire un TIME qui saute de 24 heures.
Limit: +24 h par session, et un fire récent est exigé si la dernière synchronisation date de moins d’une heure. Après dix visites, le cliquet a dix jours d’avance et les jetons des autres invités légitimes semblent expirés.
Le revers : le jeton de l’attaquant est jugé sur cette même heure falsifiée et expire donc plus tôt.
Que faire : le propriétaire passe et écrit l’heure réelle. Le cliquet ne bouge pas, car il ne recule pas, mais les nouveaux jetons invités sont émis avec un validUntil courant et fonctionnent ensuite pour tous. La reprise ne demande rien de plus que de rafraîchir la validité sur le serveur.
Severity: un déni de service temporaire jusqu’à la venue du propriétaire. Pas critique.
Requires: se trouver physiquement à portée du contrôleur pendant la fenêtre de 90 secondes qui suit le lancement de l’appairage.
Action: substituer sa propre clé publique X25519 dans la poignée de main d’appairage.
Result: le contrôleur s’appaire avec l’attaquant et non avec le propriétaire. Le propriétaire croit l’appairage réussi mais n’ouvre rien depuis son téléphone.
Défense actuelle : seulement la fenêtre temporelle et la présence physique du propriétaire — il voit que l’appairage n’a pas été confirmé et le recommence.
Hardening: ajouter un code de vérification simple — quatre chiffres sur l’écran du contrôleur ou dans le Serial — que le propriétaire confirme dans l’application. Ce code n’existe pas aujourd’hui.
Severity: la fenêtre est étroite et exige d’être à proximité pendant ces 90 secondes précises. Difficile à réaliser en pratique.
Requires: un accès physique au contrôleur et un appui long sur le bouton.
Action: La NVS est effacée, owner_secret supprimée, le contrôleur revient en mode appairage.
Result: l’attaquant appaire le contrôleur avec lui-même et le propriétaire perd l’accès.
Defence: monter le contrôleur dans un endroit protégé — dans un boîtier, sous un capot.
Severity: comme arracher un interphone d’une porte — ce n’est pas un problème cryptographique.
Requires: un accès physique et un dump de la flash, avec démarrage sécurisé et chiffrement de la flash désactivés — c’est le cas aujourd’hui.
Action: lire la NVS qui contient owner_secret.
Result: compromission totale de l’appareil et de tous les jetons invités.
Defence: activer le démarrage sécurisé et le chiffrement de la flash de l’ESP32 (efuse, irréversible). Non activé aujourd’hui.
Severity: acceptable pour un usage domestique. Sur les sites sensibles, le démarrage sécurisé est nécessaire.
Requires: être à portée BLE.
Action: se connecter et ne jamais se déconnecter. NimBLE ne supporte qu’un nombre limité de connexions simultanées.
Result: les clients légitimes ne peuvent pas se connecter tant que l’attaquant occupe un emplacement.
Défense actuelle : seulement les délais d’attente de NimBLE.
Hardening: ajouter un délai d’inactivité : si un client n’a pas effectué de FIRE valide en 5 secondes, couper la connexion.
Severity: un déni de service temporaire tant que l’attaquant est à proximité.
Action: bombarder le contrôleur d’écritures parasites.
Result: le contrôleur les rejette et les consigne dans le Serial. Son état ne change pas, et dès que l’attaquant s’éloigne tout fonctionne comme d’habitude.
Trois endroits ressemblent à une protection sans en être une. Mieux vaut les nommer que de laisser quelqu’un s’y appuyer.
L’octet d’état dans l’annonce porte une étiquette de 16 bits. Cela suffit contre une altération accidentelle dans les ondes, pas davantage : un faussaire déterminé parcourt 16 bits en quelques secondes. Cet octet ne pilote que l’indicateur ouvert/fermé sur la carte et n’accorde aucun droit ; l’étiquette reste donc courte — chaque octet supplémentaire coûte de la batterie à chaque réveil.
Les clés réservées à l’application reposent sur un secret compilé dans l’APK. Qui décompresse le fichier obtient le secret et falsifie l’attestation. Cela relève la barre face à un client web de passage, rien de plus. Le vrai contrôle pour une telle clé, c’est son rattachement à l’empreinte d’un appareil — cette vérification tourne sur le serveur et ne dépend pas d’un secret gardé par l’application.
L’expiration d’une clé invité est assurée par le contrôleur lui-même en BLE : le jeton porte une durée et le contrôleur a une horloge. Pour les autres types d’objets, il n’y a aucune durée : la clé vit jusqu’à ce que le propriétaire la révoque. L’application ne prétend pas le contraire, mais si une limite de temps apparaît un jour dans l’interface pour eux aussi, il faudra l’imposer sur le serveur et non la dessiner sur la carte.
Durcissement standard pour un usage industriel :
Rien de tout cela n’est critique pour les scénarios actuels, mais les sites sensibles méritent ce durcissement.