The next step for completing the SEV launch emulation is to implement the
"query-sev-launch-measure" feature, responsible for returning the
measurement. In this case the measurement will be computed in QEMU.

Implement sev_emulated_launch_get_measure() to emulate the LAUNCH_MEASURE
command per AMD SEV API spec section 6.5.1. It generates a random 16-byte
mnonce, computes the launch digest as SHA-256 over ld_data, then derives
the measurement via HMAC-SHA256
(TIK;0x04|| API version || build ID || policy || launch digest || mnonce).
The base64-encoded result (32-byte HMAC + 16-byte mnonce) populates
"query-sev-launch-measure" data, advancing state to LAUNCH_SECRET for
secret injection.

The TIK is supplied via 16-byte binary file specified in new
SevEmulatedProperty "tik" path; absent this, keys default to zeroed.
Example QEMU arguments with the key passed:

        -cpu "EPYC-Milan" \
        -accel tcg \
        -object sev-emulated,id=sev0,cbitpos=47,reduced-phys-bits=1,\
                                        tik=/path/to/tik.bin \
        -machine memory-encryption=sev0

Signed-off-by: Tommaso Califano <[email protected]>
---
 qapi/qom.json     |   3 +-
 target/i386/sev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/qapi/qom.json b/qapi/qom.json
index 35cda819ec..affb5024b5 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1064,11 +1064,12 @@
 # This object functionally emulates AMD SEV hardware via TCG, so
 # it does not require real hardware to run.
 #
+# @tik: binary file of the SEV TIK (default: all 0).
 # Since: 10.1.0
 ##
 { 'struct': 'SevEmulatedProperties',
   'base': 'SevGuestProperties',
-  'data': {}}
+  'data': {'*tik': 'str'}}
 
 ##
 # @SevSnpGuestProperties:
diff --git a/target/i386/sev.c b/target/i386/sev.c
index 5904f2c983..5b1c001633 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -48,6 +48,8 @@
 #include "hw/i386/e820_memory_layout.h"
 #include "qemu/queue.h"
 #include "qemu/cutils.h"
+#include "crypto/hmac.h"
+#include "crypto/random.h"
 
 OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON)
 OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST)
@@ -72,6 +74,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, 
SEV_SNP_GUEST)
 #define FAKE_API_MAJOR 1
 #define FAKE_API_MINOR 40
 
+/* SEV TEK, TIK and IV size in byte for emulation */
+#define TEK_TIK_IV_SIZE 16
+
 typedef struct QEMU_PACKED SevHashTableEntry {
     QemuUUID guid;
     uint16_t len;
@@ -197,6 +202,7 @@ struct SevGuestState {
 
 typedef struct SevEmulatedState {
     SevGuestState parent_obj;
+    uint8_t *tik;
     QEMUIOVector ld_data;
 } SevEmulatedState;
 
@@ -1431,6 +1437,89 @@ sev_launch_get_measure(Notifier *notifier, void *unused)
     trace_kvm_sev_launch_measurement(sev_guest->measurement);
 }
 
+static void
+sev_emulated_launch_get_measure(Notifier *notifier, void *unused)
+{
+    SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs);
+    SevGuestState *sev_guest = SEV_GUEST(sev_common);
+    SevEmulatedState *sev_emulated = SEV_EMULATED(sev_guest);
+
+    uint8_t prefix = 0x04;
+    uint8_t concat_data[56];
+    uint8_t mnonce[16];
+    uint8_t *hmac_raw;
+    uint8_t *ld;
+    gsize hmac_len, ld_len;
+    GByteArray *measure_raw = g_byte_array_sized_new(48);
+    QCryptoHmac *hmac = NULL;
+    Error *err = NULL;
+
+    if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) {
+        return;
+    }
+
+    /* Generate the mnonce (16B) */
+    if (qcrypto_random_bytes(mnonce, sizeof(mnonce), &err) < 0) {
+        error_report_err(err);
+        return;
+    }
+
+    /* Compute the Launch Digest (ld) */
+    if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA256, 
sev_emulated->ld_data.iov,
+                        sev_emulated->ld_data.niov, &ld, &ld_len, &err) < 0){
+        error_report_err(err);
+        return;
+    }
+    assert(ld_len == HASH_SIZE);
+
+    /*
+     * The HMAC is calculated as specified in SEV API spec in section 6.5.1:
+     * HMAC(0x04 || API_MAJOR || API_MINOR || BUILD || GCTX.POLICY
+     *           || GCTX.LD || MNONCE; GCTX.TIK)
+     */
+    concat_data[0] = prefix;
+    concat_data[1] = sev_common->api_major;
+    concat_data[2] = sev_common->api_minor;
+    concat_data[3] = sev_common->build_id;
+    memcpy(&concat_data[4],  &sev_guest->policy, 4);
+    memcpy(&concat_data[8],  ld, ld_len);
+    memcpy(&concat_data[40], mnonce, 16);
+
+    g_free(ld);
+
+    /* Initialize HMAC with TIK */
+    hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256,
+                            (uint8_t *)sev_emulated->tik,
+                            TEK_TIK_IV_SIZE,
+                            &err);
+    if (!hmac) {
+        error_report_err(err);
+        return;
+    }
+
+    /* Compute the HMAC */
+    if (qcrypto_hmac_bytes(hmac, (char *)concat_data, sizeof(concat_data),
+                            &hmac_raw, &hmac_len, &err) < 0) {
+        error_report_err(err);
+        qcrypto_hmac_free(hmac);
+        return;
+    }
+    qcrypto_hmac_free(hmac);
+    assert(hmac_len == HASH_SIZE);
+
+    /* Construct the measurement: HMAC(32B) + mnonce(16B) */
+    g_byte_array_append(measure_raw, hmac_raw, 32);
+    g_byte_array_append(measure_raw, mnonce, 16);
+
+    g_free(hmac_raw);
+
+    sev_guest->measurement =
+            g_base64_encode(measure_raw->data, measure_raw->len);
+    g_byte_array_free(measure_raw, TRUE);
+
+    sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_SECRET);
+}
+
 static char *sev_get_launch_measurement(void)
 {
     ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
@@ -1466,6 +1555,10 @@ static Notifier sev_machine_done_notify = {
     .notify = sev_launch_get_measure,
 };
 
+static Notifier sev_emu_machine_done_notify = {
+    .notify = sev_emulated_launch_get_measure,
+};
+
 static void
 sev_launch_finish(SevCommonState *sev_common)
 {
@@ -3054,6 +3147,9 @@ static int sev_emulated_init(ConfidentialGuestSupport 
*cgs, Error **errp)
     qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common);
 
     cgs->ready = true;
+
+    qemu_add_machine_init_done_notifier(&sev_emu_machine_done_notify);
+
     return 0;
 }
 
@@ -3076,6 +3172,48 @@ sev_emulated_launch_finish(SevCommonState *sev_common)
     sev_set_guest_state(sev_common, SEV_STATE_RUNNING);
 }
 
+static void sev_emulated_read_key(uint8_t *key,
+                                const char *key_filename, Error **errp)
+{
+    gsize len;
+    FILE *fp = fopen(key_filename, "rb");
+
+    if (!fp) {
+        error_setg(errp, "SEV-EMULATED: Failed to open %s", key_filename);
+        return;
+    }
+
+    len = fread(key, 1, TEK_TIK_IV_SIZE, fp);
+
+    fclose(fp);
+
+    if (len != TEK_TIK_IV_SIZE) {
+        error_setg(errp, "parameter length: key size %" G_GSIZE_FORMAT
+                   " is not equal to %u",
+                   len, TEK_TIK_IV_SIZE);
+        return;
+    }
+}
+
+static char *sev_emulated_get_tik(Object *obj, Error **errp)
+{
+    SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+    return g_memdup2(sev_emulated->tik, TEK_TIK_IV_SIZE);
+}
+
+static void sev_emulated_set_tik(
+            Object *obj, const char *key_filename, Error **errp)
+{
+    SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+    sev_emulated_read_key(
+        sev_emulated->tik,
+        key_filename,
+        errp
+    );
+}
+
 static void sev_emulated_class_init(ObjectClass *oc, const void *data)
 {
     SevCommonStateClass *scc = SEV_COMMON_CLASS(oc);
@@ -3084,6 +3222,22 @@ static void sev_emulated_class_init(ObjectClass *oc, 
const void *data)
     klass->kvm_init = sev_emulated_init;
     scc->launch_update_data = sev_emulated_launch_update_data;
     scc->launch_finish = sev_emulated_launch_finish;
+
+    /* Adding emulation specific property */
+    object_class_property_add_str(oc, "tik",
+                                sev_emulated_get_tik,
+                                sev_emulated_set_tik);
+    object_class_property_set_description(oc, "tik",
+        "Path to the binary file containing the"
+                                "SEV Transport Integrity Key (16 bytes)");
+}
+
+static void sev_emulated_instance_init(Object *obj)
+{
+    SevEmulatedState *sev_emulated = SEV_EMULATED(obj);
+
+    /* Initialize the key for emulation */
+    sev_emulated->tik = g_malloc0(TEK_TIK_IV_SIZE);
 }
 
 /* guest info specific sev/sev-es */
@@ -3100,6 +3254,7 @@ static const TypeInfo sev_emulated_info = {
     .parent = TYPE_SEV_GUEST,
     .name = TYPE_SEV_EMULATED,
     .instance_size = sizeof(SevEmulatedState),
+    .instance_init = sev_emulated_instance_init,
     .class_init = sev_emulated_class_init
 };
 
-- 
2.53.0

Reply via email to