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 <[email protected]>
---
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