Microdroid

Microdroid est un mini-OS Android qui s'exécute dans une pVM. Vous n'êtes pas obligé d'utiliser Microdroid. Vous pouvez démarrer une VM avec n'importe quel OS. Toutefois, les principaux cas d'utilisation des pVM ne consistent pas à exécuter un OS autonome, mais plutôt à offrir un environnement d'exécution isolé pour exécuter une partie d'une application avec des garanties de confidentialité et d'intégrité plus fortes que celles qu'Android peut fournir.

Avec les systèmes d'exploitation traditionnels, assurer une confidentialité et une intégrité fortes nécessite un travail considérable (souvent dupliqué), car ils ne correspondent pas à l'architecture Android globale. Par exemple, avec l'architecture Android standard, les développeurs doivent implémenter un moyen de charger et d'exécuter de manière sécurisée une partie de leur application dans la pVM, et la charge utile est créée par rapport à glibc. L'application Android utilise Bionic, la communication nécessite un protocole personnalisé sur vsock, et le débogage à l'aide d'adb est difficile.

Microdroid comble ces lacunes en fournissant une image d'OS prête à l'emploi conçue pour minimiser les efforts des développeurs afin de décharger une partie de leur application dans une pVM. Le code natif est créé par rapport à Bionic, la communication s'effectue via Binder, et il permet d'importer des APEX depuis l'hôte Android et d'exposer un sous-ensemble de l'API Android, tel que le keystore pour les opérations cryptographiques avec des clés matérielles. Dans l'ensemble, les développeurs devraient trouver Microdroid un environnement familier avec les outils auxquels ils se sont habitués dans l'OS Android complet.

Fonctionnalités

Microdroid est une version allégée d'Android avec quelques composants supplémentaires spécifiques aux pVM. Microdroid est compatible avec les éléments suivants :

  • Un sous-ensemble d'API NDK (toutes les API pour l'implémentation de libc et Bionic par Android sont fournies)
  • Fonctionnalités de débogage, telles qu'adb, logcat, tombstone et gdb
  • Démarrage validé et SELinux
  • Chargement et exécution d'un binaire, ainsi que de bibliothèques partagées, intégrés dans un APK
  • RPC Binder sur vsock et échange de fichiers avec des vérifications d'intégrité implicites
  • Chargement des APEX

Microdroid n'est pas compatible avec les éléments suivants :

  • API Java Android dans les packages android.\*

  • SystemServer et Zygote

  • Graphismes/Interface utilisateur

  • HAL

Architecture Microdroid

Microdroid est semblable à Cuttlefish, car les deux ont une architecture qui est similaire à celle d'Android standard. Microdroid se compose des images de partition suivantes regroupées dans une image de disque composite :

  • bootloader : vérifie et démarre le noyau.
  • boot.img : contient le noyau et le ramdisk d'initialisation.
  • vendor_boot.img : contient des modules de noyau spécifiques à la VM, tels que virtio.
  • super.img : se compose de partitions logiques système et fournisseur.
  • vbmeta.img : contient des métadonnées de démarrage validé.

Les images de partition sont fournies dans l'APEX de virtualisation et sont empaquetées dans une image de disque composite par VirtualizationService. En plus de l'image de disque composite de l'OS principal, VirtualizationService est responsable de la création des autres partitions suivantes :

  • payload : ensemble de partitions soutenues par les APEX et les APK d'Android
  • instance : partition chiffrée pour la persistance des données de démarrage validé par instance , telles que le sel par instance, les clés publiques APEX de confiance et les compteurs de restauration

Séquence de démarrage

La séquence de démarrage de Microdroid se produit après le démarrage de l'appareil. Le démarrage de l'appareil est abordé dans la section Micrologiciel pVM du document Architecture. La figure 1 montre les étapes qui se déroulent lors de la séquence de démarrage de Microdroid :

Flux de démarrage sécurisé de l'instance Microdroid

Figure 1. Flux de démarrage sécurisé de l'instance Microdroid

Voici une explication des étapes :

  1. Le bootloader est chargé en mémoire par crosvm et pvmfw commence à s'exécuter. Avant de passer au bootloader, pvmfw effectue deux tâches :

    • Vérifie le bootloader pour déterminer s'il provient d'une source de confiance (Google ou un OEM).
    • S'assure que le même bootloader est utilisé de manière cohérente sur plusieurs démarrages de la même pVM grâce à l'utilisation de l'image d'instance. Plus précisément, la pVM est initialement démarrée avec une image d'instance vide. pvmfw stocke l'identité du bootloader dans l'image d'instance et la chiffre. Ainsi, la prochaine fois que la pVM sera démarrée avec la même image d'instance, pvmfw déchiffrera l'identité enregistrée à partir de l'image d'instance et vérifiera qu'il s'agit de la même que celle enregistrée précédemment. Si les identités diffèrent, pvmfw refuse de démarrer.

    Le bootloader démarre ensuite Microdroid.

  2. Le bootloader accède au disque d'instance. Comme pvmfw, le bootloader dispose d'un lecteur de disque d'instance contenant des informations sur les images de partition utilisées dans cette instance lors des démarrages précédents, y compris la clé publique.

  3. Le bootloader vérifie vbmeta et les partitions chaînées, telles que boot et super, et, en cas de succès, dérive les secrets pVM de l'étape suivante. Ensuite, Microdroid transmet le contrôle au noyau.

  4. Étant donné que la super partition a déjà été vérifiée par le bootloader (étape 3), le noyau monte inconditionnellement la super partition. Comme pour Android complet, la super partition se compose de plusieurs partitions logiques montées sur dm-verity. Le contrôle est ensuite transmis au processus init, qui démarre différents services natifs. Le script init.rc est semblable à celui d'Android complet, mais adapté aux besoins de Microdroid.

  5. Le processus init démarre le gestionnaire Microdroid, qui accède à l'image d'instance. Le service de gestionnaire Microdroid déchiffre l'image à l'aide de la clé transmise à l'étape précédente et lit les clés publiques et les compteurs de restauration de l'APK client et des APEX auxquels cette pVM fait confiance. Ces informations sont utilisées ultérieurement par zipfuse et apexd lorsqu'ils montent respectivement l'APK client et les APEX demandés.

  6. Le service de gestionnaire Microdroid démarre apexd.

  7. apexd monte les APEX dans les répertoires /apex/<name>. La seule différence entre la façon dont Android et Microdroid montent les APEX est que dans Microdroid, les fichiers APEX proviennent de périphériques de blocs virtuels (/dev/vdc1, …), et non de fichiers standards (/system/apex/*.apex).

  8. zipfuse est le système de fichiers FUSE de Microdroid. zipfuse monte l'APK client, qui est essentiellement un fichier ZIP en tant que système de fichiers. En dessous, le fichier APK est transmis en tant que périphérique de blocs virtuels par la pVM avec dm-verity, comme APEX. L'APK contient un fichier de configuration avec une liste d'APEX que le développeur d'applications a demandés pour cette instance de pVM. La liste est utilisée par apexd lors de l'activation des APEX.

  9. Le flux de démarrage revient au service de gestionnaire Microdroid. Le service de gestionnaire communique ensuite avec VirtualizationService d'Android à l'aide de Binder RPC afin de pouvoir signaler des événements importants tels qu'un plantage ou un arrêt, et accepter des requêtes telles que l'arrêt de la pVM. Le service de gestionnaire lit l'emplacement du binaire principal à partir du fichier de configuration de l'APK et l'exécute.

Échange de fichiers (AuthFS)

Il est courant que les composants Android utilisent des fichiers pour l'entrée, la sortie et l'état, et qu'ils les transmettent en tant que descripteurs de fichiers (type ParcelFileDescriptor dans AIDL) avec un accès contrôlé par le noyau Android. AuthFS facilite une fonctionnalité similaire pour l'échange de fichiers entre des points de terminaison qui ne se font pas confiance mutuellement au-delà des limites de la pVM.

Fondamentalement, AuthFS est un système de fichiers distant avec des vérifications d'intégrité transparentes sur les opérations d'accès individuelles, semblables à fs-verity. Les vérifications permettent au frontend, tel qu'un programme de lecture de fichiers exécuté dans une pVM, de détecter si le backend non fiable, généralement Android, a falsifié le contenu du fichier.

Pour échanger des fichiers, le backend (fd\_server) est démarré avec une configuration par fichier spécifiant s'il est destiné à l'entrée (lecture seule) ou à la sortie (lecture-écriture). Pour l'entrée, le frontend s'assure que le contenu correspond à un hachage connu, en plus d'un arbre de Merkle pour la vérification à l'accès. Pour la sortie, AuthFS gère en interne un arbre de hachage du contenu tel qu'il est observé à partir des opérations d'écriture et peut appliquer l'intégrité lorsque les données sont relues.

Le transport sous-jacent est actuellement basé sur Binder RPC, mais cela pourrait changer à l'avenir pour optimiser les performances.

Gestion des clés

Les pVM sont fournies avec une clé de scellement stable adaptée à la protection des données persistantes et une clé d'attestation adaptée à la production de signatures dont la production par la pVM peut être vérifiée.

RPC Binder

La majorité des interfaces d'Android sont exprimées en AIDL, qui est basé sur le pilote de noyau Linux Binder. Pour prendre en charge les interfaces entre les pVM, le protocole Binder a été réécrit pour fonctionner sur des sockets, vsock dans le cas des pVM. Le fonctionnement sur des sockets permet d'utiliser les interfaces AIDL existantes d'Android dans ce nouvel environnement.

Pour configurer la connexion, un point de terminaison, tel que la charge utile de la pVM, crée un objet RpcServer, enregistre un objet racine et commence à écouter les nouvelles connexions. Les clients peuvent se connecter à ce serveur à l'aide d'un objet RpcSession, obtenir l'objet Binder et l'utiliser exactement comme un objet Binder est utilisé avec le pilote Binder du noyau.