The final step to complete SEV attestation is the implementation of "sev-inject-launch-secret", which enables injecting a secret into guest memory. The function sev_emulated_injection() performs this step. It is invoked from the existing sev_inject_launch_secret() when sev_emulated_enabled() is active, allowing the bypass of the KVM_SEV_LAUNCH_SECRET ioctl. Upon invocation, it decrypts the secret packet using AES-128-CTR with the configured TEK, extracting the IV from header bytes 4–19, and writes the decrypted payload to the guest-specified memory address. After injection, the SEV state transitions to RUNNING, completing the TCG-emulated SEV launch sequence for testing guests without AMD SEV hardware. The TEK is provided through a 16-byte binary file, similar to the TIK, and specified via the new SevEmulatedProperty "tek" path. If unspecified, the key defaults to all zeroes. Example of all QEMU arguments: -cpu "EPYC-Milan" \ -accel tcg \ -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\ tik=/path/to/tik.bin,tek=/path/to/tek.bin \ -machine memory-encryption=sev0 Signed-off-by: Tommaso Califano --- qapi/qom.json | 5 ++- target/i386/sev.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/qapi/qom.json b/qapi/qom.json index affb5024b5..405b6fc858 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1065,11 +1065,14 @@ # it does not require real hardware to run. # # @tik: binary file of the SEV TIK (default: all 0). +# +# @tek: binary file of the SEV TEK (default: all 0). # Since: 10.1.0 ## { 'struct': 'SevEmulatedProperties', 'base': 'SevGuestProperties', - 'data': {'*tik': 'str'}} + 'data': {'*tik': 'str', + '*tek': 'str' }} ## # @SevSnpGuestProperties: diff --git a/target/i386/sev.c b/target/i386/sev.c index 5b1c001633..89b3fe3507 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -48,6 +48,7 @@ #include "hw/i386/e820_memory_layout.h" #include "qemu/queue.h" #include "qemu/cutils.h" +#include "crypto/cipher.h" #include "crypto/hmac.h" #include "crypto/random.h" @@ -202,6 +203,7 @@ struct SevGuestState { typedef struct SevEmulatedState { SevGuestState parent_obj; + uint8_t *tek; uint8_t *tik; QEMUIOVector ld_data; } SevEmulatedState; @@ -2219,6 +2221,58 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) return 0; } +static int sev_emulated_injection(void *hva, guchar *data, + gsize data_sz, guchar *hdr) +{ + + SevEmulatedState *sev_emulated = + SEV_EMULATED(MACHINE(qdev_get_machine())->cgs); + uint8_t iv[TEK_TIK_IV_SIZE]; + QCryptoCipher *cipher = NULL; + g_autofree guchar *plaintext = g_new0(guchar, data_sz); + int ret = 0; + Error *err = NULL; + + /* Prepare the cipher */ + cipher = qcrypto_cipher_new( + QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CTR, + sev_emulated->tek, + TEK_TIK_IV_SIZE, + &err + ); + if (!cipher) { + error_report_err(err); + return 1; + } + + /* Extract the IV from the header */ + memcpy(iv, hdr + 4, TEK_TIK_IV_SIZE); + ret = qcrypto_cipher_setiv(cipher, iv, TEK_TIK_IV_SIZE, &err); + if (ret < 0) { + error_report_err(err); + qcrypto_cipher_free(cipher); + return 1; + } + + /* Decrypt the payload */ + ret = qcrypto_cipher_decrypt(cipher, + data, + plaintext, + data_sz, + &err); + if (ret < 0) { + error_report_err(err); + qcrypto_cipher_free(cipher); + return 1; + } + qcrypto_cipher_free(cipher); + memcpy(hva, plaintext, data_sz); + + return 0; +} + + int sev_inject_launch_secret(const char *packet_hdr, const char *secret, uint64_t gpa, Error **errp) { @@ -2273,6 +2327,15 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, trace_kvm_sev_launch_secret(gpa, input.guest_uaddr, input.trans_uaddr, input.trans_len); + /* + * If SEV emulation is enabled, skip the KVM ioctl (sev_fd == -1) and + * inject the secret directly into guest memory via + * sev_emulated_injection(). + */ + if (sev_emulated_enabled()) { + return sev_emulated_injection(hva, data, data_sz, hdr); + } + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_SECRET, &input, &error); if (ret) { @@ -3214,6 +3277,25 @@ static void sev_emulated_set_tik( ); } +static char *sev_emulated_get_tek(Object *obj, Error **errp) +{ + SevEmulatedState *sev_emulated = SEV_EMULATED(obj); + + return g_memdup2(sev_emulated->tek, TEK_TIK_IV_SIZE); +} + +static void sev_emulated_set_tek( + Object *obj, const char *key_filename, Error **errp) +{ + SevEmulatedState *sev_emulated = SEV_EMULATED(obj); + + sev_emulated_read_key( + sev_emulated->tek, + key_filename, + errp + ); +} + static void sev_emulated_class_init(ObjectClass *oc, const void *data) { SevCommonStateClass *scc = SEV_COMMON_CLASS(oc); @@ -3223,7 +3305,14 @@ static void sev_emulated_class_init(ObjectClass *oc, const void *data) scc->launch_update_data = sev_emulated_launch_update_data; scc->launch_finish = sev_emulated_launch_finish; - /* Adding emulation specific property */ + /* Adding emulation specific properties */ + object_class_property_add_str(oc, "tek", + sev_emulated_get_tek, + sev_emulated_set_tek); + object_class_property_set_description(oc, "tek", + "Path to the binary file containing the" + "SEV Transport Encryption Key (16 bytes)"); + object_class_property_add_str(oc, "tik", sev_emulated_get_tik, sev_emulated_set_tik); @@ -3238,6 +3327,7 @@ static void sev_emulated_instance_init(Object *obj) /* Initialize the key for emulation */ sev_emulated->tik = g_malloc0(TEK_TIK_IV_SIZE); + sev_emulated->tek = g_malloc0(TEK_TIK_IV_SIZE); } /* guest info specific sev/sev-es */ -- 2.53.0