TD には不揮発 UEFI 変数が存在せず mokutil --import が使えないため、TDVF イメージの CFV(変数領域)に MokList を焼き込みます。Secure Boot は有効のままです。
ホストは canonical/tdx のツール群、ゲストは tdx-guest-ubuntu-26.04-generic.qcow2 / カーネル 7.0.0-28-generic です。
$ openssl req -new -x509 -newkey rsa:2048 -nodes -days 3650 \
-subj "/CN=ima-rtmr-extend MOK/" -keyout MOK.key -out MOK.pem
$ openssl x509 -in MOK.pem -outform DER -out MOK.der
$ uuidgen
3a190b7c-4d00-4ca3-9ae4-4bf979bdc53buuidgen の値は MokList エントリの owner GUID に使います。MOK.key は 0600 で保管します。
virt-firmware の virt-fw-vars を使います。Ubuntu では python3-virt-firmware パッケージです。
$ virt-fw-vars --input /usr/share/ovmf/OVMF.inteltdx.ms.fd \
--output path/to/OVMF.inteltdx.mok.fd \
--add-mok 3a190b7c-4d00-4ca3-9ae4-4bf979bdc53b path/to/MOK.der
INFO: reading raw edk2 varstore from /usr/share/ovmf/OVMF.inteltdx.ms.fd
INFO: var store range: 0x64 -> 0x40000
INFO: create variable MokList
INFO: add MokList cert path/to/MOK.der
INFO: writing raw edk2 varstore to path/to/OVMF.inteltdx.mok.fd--set-true MokListTrusted は付けません。shim の変数テーブルで MokListTrusted には MOK_VARIABLE_INVERSE が付いており、変数が無いときに MokListTrustedRT が 0x01 になり、有るときは空にされます。カーネルは MokListTrustedRT エントリの存在だけを見ます。
出力先はホームディレクトリの外に置きます。qemu が libvirt-qemu として動き、ホームディレクトリ(0750)を辿れないためです。
$ virt-fw-dump --input path/to/OVMF.inteltdx.mok.fd
image=OVMF.inteltdx.mok.fd
volume=guid:NvData offset=0x0 size=0x84000 hlen=0x48 xoff=0x0 rev=2 blocks=132*4096 used=48.5%
nvdata=guid:AuthVars size=0x3ffb8 format=0x5a state=0xfe
variable=guid:EfiGlobalVariable nsize=0x8 dsize=0xf60 attr=0x27 name=KEK (ok)
blob: 3936 bytes
variable=guid:Shim nsize=0x10 dsize=0x34d attr=0x3 name=MokList (ok)
blob: 845 bytes
variable=guid:EfiGlobalVariable nsize=0x6 dsize=0x366 attr=0x27 name=PK (ok)
blob: 870 bytes
variable=guid:EfiImageSecurityDatabase nsize=0x6 dsize=0x17f5 attr=0x27 name=db (ok)
blob: 6133 bytes
variable=guid:EfiImageSecurityDatabase nsize=0x8 dsize=0x50ec attr=0x27 name=dbx (ok)確認する点は次の 4 つです。MokList の GUID が Shim、属性が 0x3(NON_VOLATILE | BOOTSERVICE_ACCESS)で shim の要求と一致すること、MokListTrusted が無いこと、PK / KEK / db / dbx が元のままであること、イメージサイズが 4 MiB で変わらないこと。
変更が入ったバイト範囲を確認します。
$ cmp -l /usr/share/ovmf/OVMF.inteltdx.ms.fd path/to/OVMF.inteltdx.mok.fd | head -1
4109 47 3
$ cmp -l /usr/share/ovmf/OVMF.inteltdx.ms.fd path/to/OVMF.inteltdx.mok.fd | tail -1
32948 377 1740x100c-0x80b4 です。
TDVF のメタデータをパースします。
import struct
d = open('path/to/OVMF.inteltdx.mok.fd','rb').read()
T = {0:'BFV', 1:'CFV', 2:'TD_HOB', 3:'TempMem', 4:'PermMem'}
off = d.find(b'TDVF')
_, _, ver, n = struct.unpack('<4sIII', d[off:off+16])
for i in range(n):
do, rds, ma, mds, t, attr = struct.unpack('<IIQQII', d[off+16+32*i:off+48+32*i])
print(f"{T.get(t,t):8s} offset=0x{do:06x} size=0x{rds:06x} attr=0x{attr:08x}")実行結果は次のようになります。
BFV offset=0x084000 size=0x37c000 attr=0x00000001
CFV offset=0x000000 size=0x084000 attr=0x00000000
TempMem offset=0x000000 size=0x000000 attr=0x00000000
TempMem offset=0x000000 size=0x000000 attr=0x00000000
TD_HOB offset=0x000000 size=0x000000 attr=0x00000000
TempMem offset=0x000000 size=0x000000 attr=0x00000000attr のビット 0 が MR_EXTEND で、立っているセクションが MRTD 測定され、ここでは BFV だけのようです。変更範囲 0x100c-0x80b4 は CFV(0x0-0x84000)の内側で、BFV は変わりません。MRTD は変化しません。
CFV は TDVF が SEC フェーズで sha384 して RTMR[0] に extend します。対象はイメージ先頭からの 0x84000 = 540672 バイトなので、ブート前に計算できます。
$ head -c 540672 /usr/share/ovmf/OVMF.inteltdx.ms.fd | sha384sum
42e29bef9c55c81dcc0222149306266a9a2ba745cccf7420592c645d93c3b6ef868d1779c157e82fb075d94485aad383 -
$ head -c 540672 path/to/OVMF.inteltdx.mok.fd | sha384sum
00b379113cf959170c4f343f4454590681f13dc93bda3511758c479fe1502dac5d9f00ed79626cf3994929a2e58df8c9 -shim は MokListRT の内容を PCR 14 に測ります。TDX での PCR と測定レジスタの対応(UEFI Spec 2.10 Section 38.4.1)では PCR 14 は RTMR[2] です。
まとめると、MRTD は変化せず、RTMR0 は CFV のハッシュが変わるぶん変化、RTMR2 は MokListRT の内容が変わるぶん変化します。Verifier の reference value は更新が必要です。
既存テンプレートの loader 行だけを置き換えます。
$ sed -E "s#(<loader[^>]*>)[^<]*(</loader>)#\1path/to/OVMF.inteltdx.mok.fd\2#" \
path/to/trust_domain.xml.template > trust_domain-mok.xml.template
$ diff -u path/to/trust_domain.xml.template trust_domain-mok.xml.template
@@ -8,7 +8,7 @@
<vcpu placement="static">32</vcpu>
<os>
<type arch='x86_64' machine='q35'>hvm</type>
- <loader type='rom' readonly='yes'>/usr/share/ovmf/OVMF.inteltdx.nosb.fd</loader>
+ <loader type='rom' readonly='yes'>path/to/OVMF.inteltdx.mok.fd</loader>
<boot dev='hd'/>
</os>
<features>$ ./tdvirsh new -t path/to/trust_domain-mok.xml.template
Id: 9
Name: tdvirsh-trust_domain-mok-9518cd50-4b20-4b1e-a300-2cccf33e0e49
UUID: 9518cd50-4b20-4b1e-a300-2cccf33e0e49
State: running以下はゲスト内での実行結果です。
Secure Boot と MOK の状態を見ます。
$ mokutil --sb-state
SecureBoot enabled
$ cat /sys/kernel/security/lockdown
none [integrity] confidentiality
$ cat /sys/module/module/parameters/sig_enforce
Y
$ sudo mokutil --list-enrolled | grep "Subject:"
Subject: C=GB, ST=Isle of Man, L=Douglas, O=Canonical Ltd., CN=Canonical Ltd. Master Certificate Authority
Subject: CN=ima-rtmr-extend MOK
$ sudo xxd /sys/firmware/efi/mok-variables/MokListTrustedRT
00000000: 01 .モジュールに署名します。ハッシュはゲストの CONFIG_MODULE_SIG_HASH に合わせます。
$ grep CONFIG_MODULE_SIG_HASH /boot/config-$(uname -r)
CONFIG_MODULE_SIG_HASH="sha512"
$ /lib/modules/$(uname -r)/build/scripts/sign-file sha512 MOK.key MOK.pem path/to/ima_rtmr-signed.ko
$ modinfo path/to/ima_rtmr-signed.ko | grep -E "^sig"
sig_id: PKCS#7
signer: ima-rtmr-extend MOK
sig_key: 3D:6C:48:F0:01:F9:A2:9F:72:60:22:E9:77:98:41:8F:02:BA:B3:9F
sig_hashalgo: sha512
signature: 42:23:D1:7A:A9:80:55:1C:6D:23:F0:94:79:C6:9D:FE:97:3F:77:86:同じモジュールの未署名版と署名版を比較します。
$ sudo insmod path/to/ima_rtmr.ko
insmod: ERROR: could not insert module path/to/ima_rtmr.ko: Key was rejected by service
$ sudo insmod path/to/ima_rtmr-signed.ko
insmod: ERROR: could not insert module path/to/ima_rtmr-signed.ko: Invalid parameters
$ sudo dmesg | grep -E "unsigned module|ima_rtmr"
[ 175.657341] Loading of unsigned module is rejected
[ 175.765915] ima_rtmr: loading out-of-tree module taints kernel.
[ 175.768274] ima_rtmr: using /sys/class/misc/tdx_guest/measurements/rtmr2:sha384
[ 175.794608] ima_rtmr: IMA computes no sha384 digest; boot with ima_hash=sha384署名版は署名検証を通過して module_init まで進んでいます。Invalid parameters は ima_rtmr が ima_hash=sha384 を要求するためで、署名とは無関係です。
証明書の格納先は .platform でした。
$ sudo cat /proc/keys | grep -E "\.(machine|platform|secondary)"
1693c2d9 I------ 1 perm 1f0b0000 0 0 keyring .platform: 7
19c41201 I------ 2 perm 1f0b0000 0 0 keyring .machine: empty
36d30256 I------ 1 perm 1f0f0000 0 0 keyring .secondary_trusted_keys: 2
$ sudo dmesg | grep -A1 "UEFI:MokListRT"
[ 13.224911] integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
[ 13.228861] integrity: Loaded X.509 cert 'Canonical Ltd. Master Certificate Authority: ad91990bc22ab1f517048c23b6655a268e345a63'
[ 13.232769] integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
[ 13.235424] integrity: Loaded X.509 cert 'ima-rtmr-extend MOK: 8fbec59e765fe3bade84827c22e1a66ccd286d16'upstream の get_handler_for_mok() では MokListTrustedRT があれば .machine に入るはずですが、.machine は空でした。理由は未調査です。ロード自体は通るため、判定は insmod の可否で行います。
CC イベントログをリプレイして RTMR と突き合わせます。
$ sudo python3 - <<'PY'
import struct, hashlib
d = open('/sys/firmware/acpi/tables/data/CCEL', 'rb').read()
p = 32 + struct.unpack('<I', d[28:32])[0] # skip the spec-id header event
SIZE = {4: 20, 11: 32, 12: 48, 13: 64}
regs = {}
while p < len(d) - 8:
mr, _ = struct.unpack('<II', d[p:p + 8]); p += 8
if mr == 0xFFFFFFFF:
break
cnt = struct.unpack('<I', d[p:p + 4])[0]; p += 4
dig = None
for _ in range(cnt):
alg = struct.unpack('<H', d[p:p + 2])[0]; p += 2
if alg == 12:
dig = d[p:p + 48]
p += SIZE[alg]
p += 4 + struct.unpack('<I', d[p:p + 4])[0]
if dig:
regs[mr] = hashlib.sha384(regs.get(mr, bytes(48)) + dig).digest()
for mr, name in ((1, 'rtmr0'), (2, 'rtmr1'), (3, 'rtmr2')):
actual = open(f'/sys/class/misc/tdx_guest/measurements/{name}:sha384', 'rb').read()
print(f'{name} {"match" if actual == regs[mr] else "MISMATCH"} {actual.hex()}')
PY
rtmr0 match 2eebd7634b0b89c4cb3439724cbead9604e92271564d78e5805a6f3f57ed204baf7809519220fd0e7f0c5ca3d660006e
rtmr1 match 1260b5407a3cdc44d8172ff553c51683b83d992986916094356096336cada71fb2e7201a62b432e86a2a078afc9fa8d7
rtmr2 match 4de4368bb8ab56ee7aa3df0e436f6d5dbb2090ca98d7e067438a95f985ba6515440d49adc0bd2f994cd08b4f38a6ced5イベントログ内の CFV は EV_EFI_PLATFORM_FIRMWARE_BLOB2(BlobBase 0xffc00000 / BlobLength 0x84000)で、ダイジェストは 00b379113cf959170c4f343f4454590681f13dc93bda3511758c479fe1502dac5d9f00ed79626cf3994929a2e58df8c9 です。ブート前に head -c 540672 | sha384sum で出した値と一致します。
- Intel TDX Virtual Firmware Design Guide rev 1.01
- Intel Trusted Domain eXtension (TDX) — QEMU documentation
- edk2 OvmfPkg/IntelTdx/README.md L109 — TDX で
-pflashを使わない旨 - edk2 SecTdxHelper.c
TdxHelperMeasureCfvImage()— CFV を RTMR[0] に extend - edk2 TdxMeasurementCommon.c PCR/MR 対応表
- shim mok.c
MokListの属性要求 - shim mok.c
MokListTrustedの定義 - shim mok.c
MOK_VARIABLE_INVERSEの処理 - linux keyring_handler.c
get_handler_for_db()/get_handler_for_mok() - linux machine_keyring.c
uefi_check_trust_mok_keys() - linux digsig.c
.machineのリンク制限 - virt-firmware
- canonical/tdx