Skip to content

Instantly share code, notes, and snippets.

@Hayao0819
Last active August 11, 2026 15:54
Show Gist options
  • Select an option

  • Save Hayao0819/729f91368be784dd84b6e36490961df9 to your computer and use it in GitHub Desktop.

Select an option

Save Hayao0819/729f91368be784dd84b6e36490961df9 to your computer and use it in GitHub Desktop.
TDにMOKを追加する

TDにMOKを追加する

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-4bf979bdc53b

uuidgen の値は MokList エントリの owner GUID に使います。MOK.key は 0600 で保管します。

ファームウェアの改変

コマンド

virt-firmwarevirt-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 が付いており、変数が無いときに MokListTrustedRT0x01 になり、有るときは空にされます。カーネルは 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 174

0x100c-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=0x00000000

attr のビット 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 は更新が必要です。

TDの作成

既存テンプレートの 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 parametersima_rtmrima_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 で出した値と一致します。

参考文献

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment