[libvirt] [Qemu-devel] libvirt/QEMU/SEV interaction

Brijesh Singh brijesh.singh at amd.com
Fri Sep 8 15:48:10 UTC 2017


Hi Daniel,


On 09/08/2017 09:52 AM, Daniel P. Berrange wrote:
> On Fri, Sep 08, 2017 at 01:45:06PM +0000, Relph, Richard wrote:
>> A few answers in line…
>>
>> On 9/8/17, 8:16 AM, "Daniel P. Berrange" <berrange at redhat.com> wrote:
>>
>>      On Fri, Sep 08, 2017 at 06:57:30AM -0500, Brijesh Singh wrote:
>>      > Hi All,
>>      >
>>      > (sorry for the long message)
>>      >
>>      > CPUs from AMD EPYC family supports Secure Encrypted Virtualization (SEV)
>>      > feature - the feature allows running encrypted VMs. To enable the feature,
>>      > I have been submitting patches to Linux kernel [1], Qemu [2] and OVMF [3].
>>      > We have been making some good progress in getting patches accepted upstream
>>      > in Linux and OVMF trees. SEV builds upon SME (Secure Memory Encryption)
>>      > feature -- SME support just got pulled into 4.14 merge window. The base
>>      > SEV patches are accepted in OVMF tree -- now we have SEV aware guest BIOS.
>>      > I am getting ready to take off "RFC" tag from remaining patches to get them
>>      > reviewed and accepted.
>>      >
>>      > The boot flow for launching an SEV guest is a bit different from a typical
>>      > guest launch. In order to launch SEV guest from virt-manager or other
>>      > high-level VM management tools, we need to design and implement new
>>      > interface between libvirt and qemu, and probably add new APIs in libvirt
>>      > to be used by VM management tools. I am new to the libvirt and need some
>>      > expert advice while designing this interface. A pictorial representation
>>      > for a SEV guest launch flow is available in SEV Spec Appendix A [4].
>>      >
>>      > A typical flow looks like this:
>>      >
>>      > 1. Guest owner (GO) asks the cloud provider to launch SEV guest.
>>      > 2. VM tool asks libvirt to provide its Platform Diffie-Hellman (PDH) key.
>>      > 3. libvirt opens /dev/sev device to get its PDH and return the blob to the
>>      >    caller.
>>      
>>      What sort of size are we talking about for the PDH ?
>>
>> The PDH blob is described in reference 4. It’s 0x824 bytes long… a bit over 2K bytes.
>> PDH is “Platform Diffie-Hellman” key, public portion.
>>      
>>      There's a few ways libvirt could report it
>>      
>>       1. As an XML element in the host capabilities XML
>>       2. As an XML element in the emulator capabilities XML
>>       3. Via a newly added host API
>>      
>>      > 4. VM tool gives its PDH to GO.
>>      > 5. GO provides its DH key, session-info and guest policy.
>>      
>>      Are steps 4 & 5 strictly required to be in this order, or is it
>>      possible
>>
>> Steps 4 and 5 must occur in that order. The data sent by the GO in the
>> “session info” is encrypted and integrity protected with keys that the
>> GO derives from the GO private Diffie-Hellman key and the PDH public
>> Diffie-Hellman key.
>>      
>>      What are the security requirements around the DH key, session info
>>      and guest policy ? Are any of them sensitive data which needs to be
>>      kept private from untrustworthy users. Also are all three of these
>>      items different for every guest launched, or are some of them
>>      likely to be the same for every guest ?
>>
>> The PDH is not sensitive. It is relatively static for the platform.
>> (It only changes when the platform owner chooses to change the apparent
>> identity of the platform that will actually run the virtual machine.)
>> The 128-byte session info data is encrypted and integrity protected by
>> the GO. It need not be additionally encrypted or integrity protected.
>> It should vary for EVERY guest launch.
>> The 4-byte guest policy must be sent in the clear as some components
>> may want to observe the policy bits. It may change from guest to guest,
>> but there will likely only be a few common values.
> 
> Given this, and the pretty small data sizes, I think this info could
> in fact all be provided inline in the XML config - either hex or base64
> encoded for the binary blobs.
> 
> 
> 
>>      > 8. libvirt launches the guest with "-S"
>>      
>>      All libvirt guests get launched with -S, to give libvirt chance to do some
>>      setup before starting vCPUs. Normally vCPUs are started by default, but
>>      the VIR_DOMAIN_START_PAUSED flag allows the mgmt app to tell libvirt to
>>      leave vCPUS paused.
>>      
>>      Alternatively, if libvirt sees presencese of 'sev' config for the guest,
>>      it could automatically leave it in PAUSED state regardless of the
>>      VIR_DOMAIN_START_PAUSED flag.
>>
>> While using the LAUNCH_MEASURE and LAUNCH_SECRET operations is expected
>> to be the common case, they are optional. I would recommend requiring
>> the upstream software to explicitly set the VIR_DOMAIN_START_PAUSED flag,
>> if only to minimize the dependencies…
> 
> Ok.
> 
>>      > 9. While creating the SEV guest qemu does the following
>>      >  i) create encryption context using GO's DH, session-info and guest policy
>>      >     (LAUNCH_START)
>>      >  ii) encrypts the guest bios (LAUNCH_UPDATE_DATA)
>>      >  iii) calls LAUNCH_MEASUREMENT to get the encrypted bios measurement
>>      > 10. By some interface we must propagate the measurement all the way to GO
>>      >   before libvirt starts the guest.
>>      
>>      Again, what kind of size data are we talking about for athe "measurement"
>>      blob ? a KB, 10's of KB, or more ?
>>
>> The measurement is 48 bytes…
> 
> For that small size we can definitely provide is inline in the event
> payload then.
> 
>>      My first gut instinct would be for QEMU to emit a QMP event when it has
>>      the measurement available. The event could include the actual data blob,
>>      or we can could add an explicit QMP command to fetch the data blob.
>>      
>>      Libvirt could listen for this QEMU event, and in turn emit an event from
>>      libvirt with the same data, which the mgmt tool can finally give to the
>>      GO.
>>      
>>      > 11. GO verifies the measurement and if measurement matches then it may
>>      >  give a secret blob -- which must be injected into the guest before
>>      >  libvirt starts the VM. If verification failed, GO will request cloud
>>      >  provider to destroy the VM.
>>      
>>      So we need some mechanism to provide the secret blob.  This could be
>>      done via a new libvirt API.  Alternatively, if we're using virSecret
>>      for the other stuff, the guest config XML could include the UUID of
>>      a 4th  secret. Libvirt would then watch to see when that secret has
>>      a value set, and pass that onto QEMU.
>>
>> The secret is optional… it is up to 16KB, already encrypted, and integrity
>> protected with an IV and MAC value passed with the secret. The guest
>> address the secret should be deposited at is
>> Technically, the SEV FW API allows there to be more than one secret…
>> I don’t see a strong reason to require libvirt to support more than one,
>> though.
> 
> So I think we could simply add a new API to libvirt to provide this
> data item.
> 
>>      > 12. After secret blob is injected into guest, we call LAUNCH_FINISH
>>      >   to destory the encryption context.
>>      > 13. libvirt issues "continue" command to resume the guest boot.
>>      
>>      This is as simple as the mgmt tool calling virDomainResume to unpause
>>      CPUs. We could have it such that virDomainResume checks whether the
>>      virSecret has been populated secret blob by GO, and pass it onto
>>      QEMU at this time.
>>      
>>      > Please note that the measurement value is protected with transport
>>      > encryption key (TIK) and it changes on each run. Similarly the secret blob
>>      > provided by GO does not need to be protected using libvirt/qemu APIs. The
>>      > secret is protected by TIK. From qemu and libvirt point of view these are
>>      > blobs and must be passed as-is to the SEV FW.
>>      >
>>      > Questions:
>>      > a) Do we need to add a new set of APIs in libvirt to return the PDH from
>>      > libvirt and VM tool ? Or can we use some pre-existing APIs to pass the
>>      > opaque blobs ? (this is mainly for step 3 and 6)
>>      > b) do we need to define a new xml tag to for memory-encryption ? or just
>>      > use the qemu:args tag ? (step 6)
>>      
>>      <qemu:args> is explicitly only ever for ad-hoc testing.
>>      
>>      For anything that is to be used in production deployment we must
>>      explicitly model it in the XML. So we definitely need new XML
>>      defined.
>>      
>>      > c) what existing communicate interface can be used between libvirt and qemu
>>      > to get the measurement ? can we add a new qemu monitor command
>>      > 'get_sev_measurement' to get the measurement ? (step 10)
>>      
>>      Yes, QMP commands seeem most likely.
>>      
>>      > d) how to pass the secret blob from libvirt to qemu ? should we consider
>>      > adding a new object (sev-guest-secret) -- libvirt can add the object through
>>      > qemu monitor.
>>      
>>      Yeah, that looks like a viable option too.
> 
> So I could see a flow like the following:


The flow looks good

> 
> 
>    1. mgmt tool calls  virConnectGetCapabilities. This returns an XML
>       document that includes the following
> 
>        <host>
>           ...other bits...
>          <sev>
> 	  <platform-key>...hex encoded PDH key...</platform-key>
> 	</sev>
>        </host>
> 
>    2. mgmt tool requests to start a guest calling virCreateXML(),
>       passing VIR_DOMAIN_START_PAUSED. The XML would include
> 
>        <sev>
>          <owner-key>...hex encode DH key...</owner-key>
> 	<session-info>..hex encode info...</session-info>
> 	<policy>...int32 value..</policy>
>        </sev>
> 
> 
>       if <sev> is provided and VIR_DOMAIN_START_PAUSED is missing,
>       libvirt would report an error and refuse to start the guest
> 


One thing which is not clear to me is, how do we know that we are asked
to launch SEV guest? Are you thinking that <sev> tag in the XML will
hint libvirt that GO has asked to launch a SEV guest?


>    3. Libvirt generates the QEMU cli arg to enable SEV using
>       the XML data and starts QEMU, leaving CPUs paused
> 


I am looking at [1] to get the feel for how do we model it in the XML.
As you can see I am using ad-hoc <qemu:args> to create the sev-guest
object. Currently, sev-guest object accepts the following properties:

dh-cert-file: <file containing the GO DH key>
session-info-file: <file contain the GO session info>
policy: <int32 GO policy>

I believe the new XML model will influence the property input type,
Any recommendation on how do model this part ? thank you so much.

[1] https://libvirt.org/formatdomain.html#elementsCPU


>    4. QEMU emits a SEV_MEASURE event containing the measurement
>       blob
> 
>    5. Libvirt catches the QEMU event and emits its own
>       VIR_CONNECT_DOMAIN_EVENT_SEV_MEASURE event containing
>       the measurement blob
> 
>    6. GO does its validation of the measurement
> 
>    7a  If validation failed, then virDomainDestroy() to stop QEMU
> 
>    7b  If validation succeeed
> 
>       Optionally call
> 
>           virDomainSetSEVSecret()
> 
>       providing the optional secret, then
> 
>           virDomainResume()
> 
>       to let QEMU continue
> 
> 
> 
> 
> Regards,
> Daniel
> 




More information about the libvir-list mailing list