Last active
July 23, 2024 12:51
-
-
Save chibiegg/a78214f4872230f9d8e746666aeac77c to your computer and use it in GitHub Desktop.
SEV-SNP / COCONUT-SVSM Remote Attestation Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
MIT License | |
Copyright (c) 2024 chibiegg. | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
""" | |
import random | |
import string | |
import subprocess | |
import sys | |
import tempfile | |
from fabric import Connection | |
""" | |
Requirements: | |
* `fabric` is required. (Install by `pip install fabric`) | |
* You can SSH connect to the target using public key authentication. | |
Note: | |
* This code has not been carefully considered for vulnerabilities. | |
* ex: OS Command Injection | |
""" | |
def generate_random_string(n): | |
randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)] | |
return "".join(randlst) | |
def attest_target_over_ssh(target, ek_context="0x81010000"): | |
print(f"Connecting to {target}") | |
conn = Connection(target) | |
result = conn.run("uname -a", hide=True) | |
print(f"uname -a: {result.stdout}") | |
# Create Remote dir | |
ATTESTATION_DIR = "/tmp/attestation" | |
conn.run(f"sudo mkdir -p {ATTESTATION_DIR}") | |
conn.run(f"sudo chmod 777 {ATTESTATION_DIR}") | |
# Create Local dir | |
localdir = tempfile.TemporaryDirectory("attest").name | |
print(f"localdir: {localdir}") | |
### SEV 構成証明 (アテステーションレポート) の取得と検証 | |
# (VM内) アテステーションレポートの取得 | |
result = conn.run(f"sudo snpguest report {ATTESTATION_DIR}/report.bin {ATTESTATION_DIR}/request-file.txt --random -v 2") | |
conn.get(f"{ATTESTATION_DIR}/report.bin", f"{localdir}/report.bin") | |
conn.get(f"{ATTESTATION_DIR}/request-file.txt", f"{localdir}/request-file.txt") | |
# TODO: VMPL0 (SVSM) のアテステーションレポートの取得 (vTPMのEK生成後に実施) | |
# (アテスター) 検証用証明書の取得とアテステーションレポートの検証 | |
subprocess.check_call(f"mkdir {localdir}/certs", shell=True) | |
subprocess.check_call(f"snpguest fetch ca -e vcek der milan {localdir}/certs", shell=True) | |
subprocess.check_call(f"snpguest fetch vcek der milan {localdir}/certs {localdir}/report.bin", shell=True) | |
subprocess.check_call(f"snpguest verify certs {localdir}/certs", shell=True) | |
subprocess.check_call(f"snpguest verify attestation {localdir}/certs {localdir}/report.bin", shell=True) | |
# TODO: アテステーションレポートの内容を確認する必要がある (e.g., ファームウェアのハッシュ値等) | |
# TODO: VMPL0 (SVSM) のアテステーションレポートの検証 | |
### vTPMを利用したMesuerd Bootの検証 | |
# (VM内) EKの生成とEKpubの取得 | |
result = conn.run(f"sudo tpm2_createek -Q -c {ek_context} -G rsa -u {ATTESTATION_DIR}/ek.pub") | |
print(f"tpm2_createek: {result.stdout}") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/ek.pub") | |
conn.get(f"{ATTESTATION_DIR}/ek.pub", f"{localdir}/ek.pub") | |
# (VM内) AKの生成とAKpubの取得 | |
result = conn.run( | |
f"sudo tpm2_createak --ek-context={ek_context} --ak-context={ATTESTATION_DIR}/ak.ctx --key-algorithm=rsa --hash-algorithm=sha256 --signing-algorithm=rsassa --public={ATTESTATION_DIR}/ak.pub --ak-name={ATTESTATION_DIR}/ak.name", | |
) | |
print(f"tpm2_createak: {result.stdout}") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/ak.pub") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/ak.name") | |
conn.get(f"{ATTESTATION_DIR}/ak.pub", f"{localdir}/ak.pub") | |
conn.get(f"{ATTESTATION_DIR}/ak.name", f"{localdir}/ak.name") | |
# (アテスター) AKとEKの検証のために暗号化されたチャレンジを生成する | |
aiksecret = generate_random_string(16).encode("ascii") | |
with open(f"{localdir}/ak_secret", "wb") as f: | |
f.write(aiksecret) | |
akname = "" | |
with open(f"{localdir}/ak.name", "rb") as f: | |
n = f.read() | |
akname = n.hex() | |
print(f"akname: {akname}") | |
cmd = [ | |
"tpm2_makecredential", | |
"--tcti", | |
"none", | |
f"--public={localdir}/ek.pub", | |
f"--secret={localdir}/ak_secret", | |
f"--credential-blob={localdir}/mkcred.out", | |
f"--name={akname}", | |
] | |
cmd = " ".join(cmd) | |
print(cmd) | |
subprocess.check_call(cmd, shell=True) | |
conn.put(f"{localdir}/mkcred.out", f"{ATTESTATION_DIR}/mkcred.out") | |
# (VM内) アテスターから送り込んだ暗号化されたチャレンジを復号する | |
conn.run(f"sudo tpm2_startauthsession --policy-session -S session.ctx") | |
conn.run(f"sudo tpm2_policysecret -S session.ctx -c e") | |
conn.run(f"sudo tpm2_activatecredential -c {ATTESTATION_DIR}/ak.ctx -C {ek_context} -i {ATTESTATION_DIR}/mkcred.out -o {ATTESTATION_DIR}/actcred.out -P 'session:session.ctx'") | |
conn.run(f"sudo tpm2_flushcontext session.ctx") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/actcred.out") | |
conn.get(f"{ATTESTATION_DIR}/actcred.out", f"{localdir}/actcred.out") | |
# (アテスター) 復号されたチャレンジが事前に生成した値と一致するか検証する | |
with open(f"{localdir}/actcred.out", "rb") as f: | |
actual = f.read() | |
print("Expected: {}".format(aiksecret.hex())) | |
print(" Actual: {}".format(actual.hex())) | |
if actual != aiksecret: | |
raise ValueError("Activate credential mismatched") | |
# (VM内) vTPMによって記録されたPCR値を取得する | |
nonce = generate_random_string(16).encode("ascii") | |
with open(f"{localdir}/nonce", "wb") as f: | |
f.write(nonce) | |
conn.put(f"{localdir}/nonce", f"{ATTESTATION_DIR}/nonce") | |
conn.run(f"sudo tpm2_quote --key-context {ATTESTATION_DIR}/ak.ctx --pcr-list sha1:0,1,2,3,4,5,6,7,8,9 --message {ATTESTATION_DIR}/pcr_quote.plain --signature {ATTESTATION_DIR}/pcr_quote.signature --qualification {ATTESTATION_DIR}/nonce --hash-algorithm sha256 --pcr {ATTESTATION_DIR}/pcr.bin") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/pcr_quote.plain") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/pcr_quote.signature") | |
conn.run(f"sudo chmod 644 {ATTESTATION_DIR}/pcr.bin") | |
conn.get(f"{ATTESTATION_DIR}/pcr_quote.plain", f"{localdir}/pcr_quote.plain") | |
conn.get(f"{ATTESTATION_DIR}/pcr_quote.signature", f"{localdir}/pcr_quote.signature") | |
conn.get(f"{ATTESTATION_DIR}/pcr.bin", f"{localdir}/pcr.bin") | |
# (アテスター) vTPMから取得したPCRとPCRの署名値をAKpubを用いて検証する | |
cmd = [ | |
"tpm2_checkquote", | |
"-u", | |
f"{localdir}/ak.pub", | |
"-m", | |
f"{localdir}/pcr_quote.plain", | |
"-s", | |
f"{localdir}/pcr_quote.signature", | |
"--qualification", | |
f"{localdir}/nonce", | |
] | |
cmd = " ".join(cmd) | |
print(cmd) | |
subprocess.check_call(cmd, shell=True) | |
# TODO: PCRの値が事前に確認した期待する値になっているか確認する | |
print("AIK and PCR is verified!!") | |
# TODO: 全ての検証に成功したらFDEの鍵をVMに送り、OSの起動を継続する | |
if __name__ == "__main__": | |
attest_target_over_ssh(sys.argv[1]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Connecting to ubuntu@192.168.122.77 | |
uname -a: Linux guest 6.8.0-coconut-svsm-sevsnp+ #6 SMP PREEMPT_DYNAMIC Mon Jun 10 08:19:20 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux | |
localdir: /tmp/tmpbms63v_cattest | |
The AMD ARK was self-signed! | |
The AMD ASK was signed by the AMD ARK! | |
The VCEK was signed by the AMD ASK! | |
Reported TCB Boot Loader from certificate matches the attestation report. | |
Reported TCB TEE from certificate matches the attestation report. | |
Reported TCB SNP from certificate matches the attestation report. | |
Reported TCB Microcode from certificate matches the attestation report. | |
Chip ID from certificate matches the attestation report. | |
VEK signed the Attestation Report! | |
tpm2_createek: | |
loaded-key: | |
name: 000b3ab69d1b329beb57d6f99a33a770e8c6c97c13ed8f0a5220ecb307db50a9393b | |
qualified name: 000ba63febdf098d5d61d1b7213937c745cf2cd46f13aa53f3011a65782855bdeaed | |
tpm2_createak: loaded-key: | |
name: 000b3ab69d1b329beb57d6f99a33a770e8c6c97c13ed8f0a5220ecb307db50a9393b | |
qualified name: 000ba63febdf098d5d61d1b7213937c745cf2cd46f13aa53f3011a65782855bdeaed | |
akname: 000b3ab69d1b329beb57d6f99a33a770e8c6c97c13ed8f0a5220ecb307db50a9393b | |
tpm2_makecredential --tcti none --public=/tmp/tmpbms63v_cattest/ek.pub --secret=/tmp/tmpbms63v_cattest/ak_secret --credential-blob=/tmp/tmpbms63v_cattest/mkcred.out --name=000b3ab69d1b329beb57d6f99a33a770e8c6c97c13ed8f0a5220ecb307db50a9393b | |
WARN: Tool optionally uses SAPI. Continuing with tcti=none | |
837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa | |
certinfodata:6a4238306b31704557714f77686a7462 | |
Expected: 6a4238306b31704557714f77686a7462 | |
Actual: 6a4238306b31704557714f77686a7462 | |
quoted: ff54434780180022000ba63febdf098d5d61d1b7213937c745cf2cd46f13aa53f3011a65782855bdeaed00107048435a375a6b546e4d5a44707069690000000000151041000000010000000001201706190016363600000001000403ff030000203d2e59b87be259e0a8284795a93d8861664dca61b86e01dc656da70105f3d656 | |
signature: | |
alg: rsassa | |
sig: 5567a21b363bc245f5cab7b0100c4fa7d2254dcf240de1d198e4a68fcb5222ebd1f1668c82baebc4543c75de63ae01437b7ee75c77f58c7a68aa5ec6f0888480d15313d933d1a6d98688ed0f210b49878a64d716f7acca084b3b80b32e0ca8ffe1508bd969d62d78cbcfef77e4bd47dfe1ebbea9f3f912295df2bb1beaf35ea5c9f10effa7e5b7cac3ab777b3288dc7986b8bf2e5e100708383c187fd242d40a55aca7cb93d0435f126d97e41c89cec272107dca342dcb4e1279db5ddb7978cc56f4bf12bba42d0bba516dc6ecf12049a039191a474536ed94c3c9da60215b2ef0e665d08dee7c23fbf1bdcc40ef0a12e90bc218007a79a4894bef89b3202d33 | |
pcrs: | |
sha1: | |
0 : 0x1A9995D2E91C691AEF5FC31402EF148282A3F3E0 | |
1 : 0x4CCF0FC04FF0D55663537F8C9C365E478E9A927A | |
2 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236 | |
3 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236 | |
4 : 0x175F4319FD7AC683BF49F2E7B837630E4FA8603F | |
5 : 0xAE9CF30F68D5BE8CBB7FF6D898DD66FB3602D738 | |
6 : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236 | |
7 : 0x1DBFFD353397AFBE91B5F90BFC53AF84D3E6B234 | |
8 : 0xB83C498178CB7C2B3A3E81BEBB37355BE05E61E6 | |
9 : 0x4BB31A8E9322E5F8F69776F1AD79D033956C3A08 | |
calcDigest: 3d2e59b87be259e0a8284795a93d8861664dca61b86e01dc656da70105f3d656 | |
tpm2_checkquote -u /tmp/tmpbms63v_cattest/ak.pub -m /tmp/tmpbms63v_cattest/pcr_quote.plain -s /tmp/tmpbms63v_cattest/pcr_quote.signature --qualification /tmp/tmpbms63v_cattest/nonce | |
sig: 5567a21b363bc245f5cab7b0100c4fa7d2254dcf240de1d198e4a68fcb5222ebd1f1668c82baebc4543c75de63ae01437b7ee75c77f58c7a68aa5ec6f0888480d15313d933d1a6d98688ed0f210b49878a64d716f7acca084b3b80b32e0ca8ffe1508bd969d62d78cbcfef77e4bd47dfe1ebbea9f3f912295df2bb1beaf35ea5c9f10effa7e5b7cac3ab777b3288dc7986b8bf2e5e100708383c187fd242d40a55aca7cb93d0435f126d97e41c89cec272107dca342dcb4e1279db5ddb7978cc56f4bf12bba42d0bba516dc6ecf12049a039191a474536ed94c3c9da60215b2ef0e665d08dee7c23fbf1bdcc40ef0a12e90bc218007a79a4894bef89b3202d33 | |
AIK and PCR is verified!! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment