Contexte et mécanisme SEV‑SNP
AMD Secure Encrypted Virtualization (SEV) chiffre la mémoire d’une VM avec une clé propre au guest, inaccessible à l’hyperviseur. SEV‑ES étend ce chiffrement à l’état des registres CPU, tandis que SEV‑SNP (Secure Nested Paging) ajoute un contrôle d’appartenance et de mappage des pages afin d’empêcher un hyperviseur malveillant de substituer ou de remapper des pages privées. Le firmware SNP, exécuté sur le processeur sécurisé d’AMD, permet au guest de demander un rapport signé contenant la mesure de lancement (SHA‑384), la politique du guest, les versions de sécurité de la plateforme et 64 octets de REPORT_DATA fourni par le vérificateur.
Lors du démarrage, le host charge le firmware de démarrage (UEFI Google basé sur OVMF) puis, éventuellement, un SVSM (Coconut) qui héberge des services comme un TPM virtuel. Le processus de lancement se compose de trois appels firmware : SNP_LAUNCH_START, SNP_LAUNCH_UPDATE (envoi de pages mémoire) et SNP_LAUNCH_FINISH. Chaque page normale est hachée avec son adresse, son type et ses permissions ; le hachage cumulé forme la mesure de lancement. Cette mesure est ensuite copiée dans le rapport et reste immuable pendant l’exécution de la VM.
Obtention du rapport depuis Linux
Le driver Linux expose l’interface SNP via le fichier spécial /dev/sev-guest. Un processus utilisateur envoie l’ioctl SNP_GET_REPORT (code 0xc0205300) avec une structure contenant le défi de 64 octets (REPORT_DATA) et le niveau de privilège VMPL souhaité (0‑3). Le firmware renvoie un en‑tête de 32 octets suivi d’un rapport brut de 1184 octets. Le code suivant, écrit en Zig 0.17.0, montre la séquence complète :
const std = @import("std");
const linux = std.os.linux;
const ReportRequest = extern struct {
user_data: [64]u8,
vmpl: u32,
reserved: [28]u8 = @splat(0),
};
const GuestRequest = extern struct {
msg_version: u8 = 1,
padding: [7]u8 = @splat(0),
req_data: u64,
resp_data: u64,
exitinfo2: u64 = 0,
};
pub fn fetchReport(fd: i32, challenge: [64]u8, vmpl: u32) ![1184]u8 {
var request: ReportRequest = .{ .user_data = challenge, .vmpl = vmpl };
var response: [4000]u8 = @splat(0);
var call: GuestRequest = .{
.req_data = @intFromPtr(&request),
.resp_data = @intFromPtr(&response),
};
const SNP_GET_REPORT = 0xc0205300;
const result = linux.ioctl(fd, SNP_GET_REPORT, @intFromPtr(&call));
if (linux.errno(result) != .SUCCESS or call.exitinfo2 != 0) {
return error.ReportRequestFailed;
}
const status = std.mem.readInt(u32, response[0..4], .little);
const size = std.mem.readInt(u32, response[4..8], .little);
if (status != 0 or size != 1184) return error.InvalidResponse;
return response[32..][0..1184].*;
}
Le défi fourni par le vérificateur apparaît dans le champ REPORT_DATA du rapport, garantissant que chaque rapport est lié à une requête fraîche et empêchant la réutilisation d’un rapport ancien.
Analyse du code Zig et validation cryptographique
Le module zig-sev-guest encapsule la récupération, le décodage et la vérification du rapport. Après extraction, la signature ECDSA P‑384 du rapport est vérifiée à l’aide d’OpenSSL contre le certificat de chaîne fourni par AMD (racines embarquées). La validation inclut : la concordance des versions de sécurité de la plateforme, l’identifiant du processeur (si présent), la politique du guest, la mesure de lancement attendue (fourni par Google sous forme d’« launch endorsement ») et le défi. Toute divergence – par exemple une mesure de lancement différente, un VMPL non autorisé ou des versions de sécurité inférieures – entraîne le rejet du rapport.
Implications pour l’attestation distante
Le flux décrit permet à un service externe (le vérificateur) de s’assurer que le code initialement chargé dans la VM correspond exactement à une image approuvée, même si le système d’exploitation ou les applications ultérieures ne sont pas couverts par la mesure de lancement. Les environnements cloud tels que Google Cloud publient les mesures attendues pour leurs firmwares UEFI et SVSM, facilitant la comparaison automatisée. Cependant, la protection ne s’étend pas aux données manipulées après le lancement ; les développeurs doivent donc combiner SEV‑SNP avec des mécanismes d’intégrité supplémentaires (ex. : signatures d’applications, enclaves). Enfin, le modèle de niveaux VMPL montre que le simple fait d’être root dans Linux ne confère pas le même privilège que le code exécuté au niveau VMPL 0, renforçant la séparation entre le guest et les services de confiance comme le vTPM.