11.13. Appairage et liaison (bonding)

Tout ce qui a été présenté jusqu’ici fait transiter des octets sur les ondes radio en clair. N’importe qui disposant d’un ordinateur portable compatible BLE dans la même pièce peut écouter les canaux de publicité, suivre la séquence de saut d’une connexion ouverte et lire chaque lecture, écriture et notification qui passe. Pour la plupart des données publiques de capteurs (niveau de batterie, température ambiante), cela ne pose pas de problème. Pour tout ce que les deux extrémités souhaitent garder privé – un registre de contrôle qui arme un relais, un mot de passe, une mesure qui ne devrait pas être largement diffusée – la liaison doit être chiffrée, et idéalement la caméra doit savoir avec qui elle communique.

Le BLE fournit les deux grâce à l”appairage (pairing) et à la liaison (bonding).

11.13.1. Appairage, liaison, chiffrement

Trois concepts étroitement liés :

  • Le chiffrement est l’objectif final. Une fois la liaison chiffrée, chaque paquet sur les canaux de données n’est déchiffrable que par les deux extrémités ; un espion ne voit que du bruit.

  • L”appairage est la procédure que les deux extrémités exécutent pour se mettre d’accord sur les clés utilisées par le chiffrement. Il s’agit d’un échange unique qui produit un matériel de clé partagé que la couche de liaison branche sur son moteur de chiffrement.

  • La liaison (bonding) est le choix de conserver les clés dans un stockage non volatile une fois l’appairage terminé, afin que la prochaine connexion entre les deux mêmes appareils saute l’appairage et passe directement au chiffrement.

En clair : l’appairage consiste à « se présenter » ; la liaison consiste à « se souvenir de cette présentation » ; le chiffrement consiste à « parler en privé à partir de maintenant ».

Deux colonnes intitulées "Central" et "Peripheral". Une ligne en pointillés près du haut intitulée "BLE connection open (unencrypted)". En dessous, trois flèches : "pairing request" du central vers le périphérique, "key exchange" dans les deux directions, "pairing complete" vers l'avant. Une deuxième ligne en pointillés en dessous intitulée "link encrypted". Deux flèches bidirectionnelles épaisses transportent "encrypted GATT traffic". Une boîte optionnelle "store keys to flash" sur le côté, intitulée "bonding".

Le déroulement de l’appairage par-dessus une connexion BLE ouverte. Une fois l’échange de clés terminé, la couche de liaison chiffre chaque paquet suivant. La liaison (bonding) est l’étape supplémentaire consistant à écrire les clés dans la mémoire flash.

11.13.2. LE Secure Connections

L’échange de clés moderne utilisé par le BLE est LE Secure Connections, construit sur Elliptic Curve Diffie-Hellman. Les deux côtés génèrent une paire de clés temporaire, échangent les moitiés publiques et combinent le résultat avec leurs propres clés privées pour arriver au même secret partagé – un secret qu’un espion ne peut pas calculer même avec un enregistrement complet de l’échange.

L’ancienne méthode LE Legacy est moins sûre (un espion disposant de l’échange complet peut généralement récupérer la clé) et n’existe que pour la compatibilité ascendante avec les anciens périphériques. La valeur par défaut de aioble est la méthode moderne (le_secure=True) ; conservez-la.

11.13.3. Initier l’appairage

Un central s’appaire en appelant aioble.DeviceConnection.pair() sur une connexion déjà ouverte

async with await device.connect() as connection:
    await connection.pair(bond=True, le_secure=True, mitm=False)
    # ... GATT work, now over an encrypted link ...

Une fois que pair retourne, les attributs encrypted, authenticated, bonded et key_size de la connexion reflètent ce qui a été négocié.

Les quatre arguments nommés les plus utiles :

  • bond=True – enregistre les clés résultantes dans la mémoire flash afin que la prochaine connexion entre les deux mêmes appareils saute la poignée de main d’appairage. Valeur par défaut True.

  • le_secure=True – utilise LE Secure Connections. Valeur par défaut True. Laissez-le activé.

  • mitm=False – indique s’il faut exiger une protection contre les attaques de l’homme du milieu (man-in-the-middle). Cela nécessite un canal hors bande (un code numérique affiché d’un côté et confirmé de l’autre, un code d’accès saisi, …) afin que l’utilisateur puisse vérifier que les deux appareils impliqués dans la poignée de main d’appairage sont bien ceux qu’il pense. Vaut False par défaut (pas de protection MITM – un espion passif ne peut pas lire la liaison, mais un attaquant qui redirige activement les connexions pourrait s’appairer à la place). Réglez sur True pour tout ce qui est sensible, mais sachez que cela exige que le périphérique prenne réellement en charge une capacité d’entrée/sortie.

  • io=3 – la capacité d’entrée/sortie que l’appareil déclare. La spécification Bluetooth en définit cinq : 0 affichage uniquement, 1 affichage + oui/non, 2 clavier uniquement, 3 aucune entrée aucune sortie, 4 clavier + affichage. Une caméra sans interface utilisateur rapporte généralement 3 ; si la caméra elle-même dispose d’un écran, l’application pourrait afficher la confirmation numérique et utiliser 1. La combinaison des capacités d’entrée/sortie des deux côtés détermine si une véritable protection MITM est réalisable.

Les périphériques n’appellent pas pair eux-mêmes – ils répondent à ce que le central initie. La question de savoir si le chiffrement est requis pour une caractéristique donnée est une propriété de la manière dont elle est déclarée dans la base de données GATT ; les bits d’accès exigeant le chiffrement font partie de l’API bas niveau bluetooth et ne sont pas actuellement exposés via le constructeur de caractéristique de aioble.

11.13.4. La liaison (bonding) – et où vivent les clés

Lorsque bond=True, aioble écrit les clés dans un fichier JSON sur le système de fichiers local. Le nom de fichier par défaut est ble_secrets.json, écrit relativement au répertoire de travail courant. Sur une caméra fraîchement démarrée, _boot.py a déjà choisi ce répertoire : /sdcard lorsqu’une carte est montée, /flash sinon – de sorte que le fichier se retrouve à /sdcard/ble_secrets.json ou /flash/ble_secrets.json. Le fichier contient les entrées nécessaires pour re-chiffrer la liaison la prochaine fois que le pair lié se reconnecte, y compris l’adresse d’identité du pair.

Une asymétrie à garder à l’esprit : l”enregistrement se produit automatiquement à mesure que les clés changent, mais le chargement du fichier au prochain démarrage, non. Appelez aioble.security.load_secrets() une fois au démarrage (avant tout appairage ou toute publicité) afin que les pairs précédemment liés soient reconnus

import aioble
aioble.security.load_secrets()        # default path: ble_secrets.json

Après cela, la prochaine fois qu’un pair lié se présente, aioble réutilise les clés stockées et la liaison devient chiffrée sans poignée de main supplémentaire.

Deux conséquences pratiques du stockage des clés dans la mémoire flash :

  • Oublier un appareil. Supprimez ble_secrets.json (ou retirez l’entrée concernée) pour oublier tous les pairs liés, puis ré-appairez à partir de zéro.

  • L’accès physique divulgue les clés. Quiconque a accès au système de fichiers de la caméra peut lire le JSON. C’est le même type de contrainte qui est apparue côté réseau avec les clés TLS (Exploitation : clés, expiration et dépannage) : utilisez des clés propres à chaque appareil, considérez toute clé stockée comme récupérable, et reposez-vous sur la possibilité de révoquer (ici, la suppression de la liaison côté central) plutôt que sur le secret de la clé.

11.13.5. Ce que le chiffrement garantit – et ce qu’il ne garantit pas

Une liaison appairer-puis-chiffrer offre, par ordre de robustesse :

  • Confidentialité. Toujours. Un espion ne peut pas lire les octets.

  • Intégrité. Toujours. Les paquets modifiés échouent à la vérification de chiffrement authentifié de la couche de liaison et sont rejetés.

  • Authentification. Uniquement avec mitm=True et une capacité d’entrée/sortie adéquate. Sans cela, un homme du milieu qui aurait intercepté l’échange d’appairage initial aurait pu s’insérer ; sans protection MITM, il n’y a aucun moyen pour les deux côtés de le savoir.

Pour la plupart des cas d’usage des caméras – un téléphone qui s’appaire une fois avec la caméra, puis se reconnecte plus tard – mitm=False suffit généralement, car l’appairage initial a lieu dans un environnement contrôlé (l’utilisateur tient les deux appareils dans la même pièce). Pour les applications où un appareil appairé pourrait rencontrer la caméra pour la première fois à grande distance ou via un intermédiaire non fiable, MITM est le bon réglage.

11.13.6. Quand l’appairage est la mauvaise réponse

L’appairage a un coût réel : quelques secondes d’échange à la première connexion, une utilisation persistante de la mémoire flash pour chaque appareil lié, et la procédure de récupération consistant à « oublier la liaison » si quelque chose tourne mal. Pour des données réellement publiques – des relevés de capteur ambiant publiés sous forme de balise, un panneau affichant son nom, tout ce qui ne change pas le monde du fait d’être lu ou écrit – la bonne réponse est de ne pas chiffrer du tout, et de laisser n’importe quel scanner à proximité lire les valeurs.

Pour tout le reste, connection.pair(bond=True) sur le central est l’ajout d’une seule ligne qui transforme la liaison d’un canal public en un canal privé.