科学・技術
AMD SEVレポートの取得(Zigを使用して)
Fetching AMD SEV Reports (With Zig) (bschaatsbergen.com)
要約
この記事は、AMDのSEV(Secure Encrypted Virtualization)技術、特にSEV-SNP(Secure Nested Paging)を利用して、仮想マシン(VM)のメモリとレジスタ状態を暗号化し、ハイパーバイザーから保護する方法について説明しています。SEV-SNPは、VMの起動状態と設定の証拠として、AMDセキュアプロセッサによって署名されたレポートをゲストVMが要求できる機能を提供します。筆者はZig言語を使用して、Linuxの`/dev/sev-guest`インターフェースを通じてこのレポートを取得し、検証するプロセスを解説しています。
全文翻訳
通常の仮想マシンでは、ハイパーバイザーはゲストのメモリを読むことができます。AMD SEV(Secure Encrypted Virtualization)は、ハイパーバイザーが受け取らないVMごとのキーでそのメモリを暗号化します。SEV-ESは、保存されたCPUレジスタ状態にも保護を拡張します。
SEV-SNP(Secure Nested Paging)は、悪意のあるハイパーバイザーがプライベートページを置き換えたり再マッピングしたりするのを防ぐために、メモリの所有権とマッピングに対するチェックを追加します。これはゲストをホストから保護しますが、ゲスト内のソフトウェアを信頼できるものにしたり、ホストがVMを停止するのを防いだりするわけではありません。
SNPを使用すると、ゲストはAMDセキュアプロセッサによって署名されたレポートを要求できます。これは、VMの起動状態と設定の証拠を提供します。これには、起動測定値、ゲストポリシー、プラットフォームセキュリティバージョン、および呼び出し元が供給した64バイトのREPORT_DATAが含まれます。
VMが起動する前に、ホストはその開始ソフトウェアをメモリにロードします。これには、VMのブートファームウェアが含まれます。これは、VMのブート環境を初期化し、オペレーティングシステムのブートローダーに制御を渡す起動ソフトウェアです。Google Cloudでは、Open Virtual Machine Firmware(OVMF)に基づいたGoogle管理のUEFIファームウェアがこれに該当します。
開始ソフトウェアには、Coconut SVSM(仮想TPMなどのサービスをホストする別のプログラム)も含まれる場合があります。UEFIファームウェアは引き続きOSを起動します。SVSMは、OS自体から保護する必要があるサービスを提供します。
AMDのSNPファームウェアはセキュアプロセッサ上で実行されます。ホストはSNP_LAUNCH_STARTで起動を開始し、次にメモリページと呼ばれるチャンクで開始イメージをSNP_LAUNCH_UPDATE経由で供給します。通常のコードまたはデータページの各ページについて、ファームウェアはインストールしたバイト、そのゲストメモリアドレス、ページタイプ、および権限を前のハッシュとともにハッシュします。AMDのファームウェアは、この実行中のハッシュを保護された状態に維持します。ホストは置き換えられた値を供給できません。
SNP_LAUNCH_FINISHは起動を閉じます。最終的なSHA-384ハッシュは起動測定値、つまりその初期イメージのフィンガープリントであり、SVSMが存在する場合はそれも含まれます。後で、ファームウェアはこの保存された値をレポートにコピーします。VMが実行されている間、これは固定されたままです。
リモートアテステーションを実行する際、検証者はそれを、信頼する開始ソフトウェアと起動設定の期待されるフィンガープリントと比較します。Googleは、ファームウェアの期待される測定値を含む署名付き起動承認を公開しています。ファームウェアバイトを変更すると、フィンガープリントが変更されます。これらのページを測定対象外としてマークすると、そのコンテンツのハッシュはスキップされますが、ハッシュに含まれるページタイプが変更されます。いずれにしても、結果は期待されるフィンガープリントと異なるため、検証者はアテステーションを拒否できます。
ファームウェアがロードするカーネルおよびアプリケーションは、このフィンガープリントによって自動的にカバーされません。
Linuxでのレポート取得
Linuxは/dev/sev-guestでSNPゲストインターフェースを公開します。アプリケーションはSNP_GET_REPORT ioctlを送信し、ドライバーはAMD SNPファームウェアとの保護された交換を処理します。
レポートデータとともに、リクエストはVM権限レベル、またはVMPL(0から3まで、0が最も特権的)を供給します。これらのレベルは、Linuxが常にVM内で最も特権的なソフトウェアであるとは限らないため重要です。例えば、Coconut SVSMは、ゲストカーネルから分離された仮想TPMをホストするためにその分離を使用できます。Linuxでroot権限を持っていても、VMPL0ではありません。ゲストがサポートするVMPLを使用してください。
以下は、LinuxのゲストAPIのレイアウトを使用したZig(0.17.0)でのリクエストです。fdは、x86-64 Linux SNPゲスト内の開かれた/dev/sev-guestディスクリプタです。
```zig
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;
// レポートは32バイトのファームウェア応答ヘッダーの後に続きます。
return response[32..][0..1184].*;
}
```
検証者からの新しい64バイトのチャレンジとゲストのVMPLを渡します。チャレンジはREPORT_DATAで返されるため、検証者は古いレポートを拒否できます。結果は生のレポートです。その署名とポリシーはまだチェックする必要があります。
私は、取得、レポートの解析、および検証をzig-sev-guestにパッケージ化したため、呼び出し元はこれらのバッファを直接扱う必要はありません。
Coconut SVSMおよびOpenHCLを介したレポートの取得
Coconut SVSMとMicrosoftのOpenHCLは設計が異なりますが、どちらも機密VM内でvTPMのような特権サービスを実行します。SNPでは、AMDハードウェアはそのメモリ(TPMのプライベートキーを含む)をセキュアプロセッサによって管理されるVMごとのキーで暗号化します。ゲストカーネルは、SVSMまたはOpenHCLよりも特権の低いVMPLで実行されます。SNPのページ権限は、vTPMのプライベートキーを保持するページを読み取ることをブロックします。
ゲストは、公開キーのハッシュを含むレポートに署名するようにAMDに要求できます。それは、キーが保護されたvTPMに属することを証明しません。SVSMとOpenHCLは、ホストしているTPMの公開キーを使用してリクエストを構築します。したがって、アプリケーションはそれらを通じてこのレポートを取得します。
アプリケーション/dev/sev-guest
Linux設定
fs
vTPMインターフェース
Coconut SVSM
OpenHCL
SNPファームウェア
SNPファームウェアでバックエンド化された3つのリクエストパス。
証拠のチェック
zig-sev-guestでは、OpenSSLを使用して署名証明書のチェーンを埋め込まれたAMDルートに対して検証し、レポートのECDSA P-384署名をチェックします。証明書のセキュリティバージョンと、存在する場合はチップIDもレポートと一致する必要があります。
次に、ポリシー検証は、レポートを期待される起動測定値、REPORT_DATA、VMPL、および最小プラットフォームセキュリティバージョンと比較します。デバッグ、SMT、および移行は、明示的に許可されていない限り拒否されます。
verify.andValidateは、両方のステップを同じレポートに対して実行します。呼び出し元は期待される値を供給し、新しいチャレンジを含め、vTPM固有のクレームと引用を個別にチェックします。