qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2] target/i386/sev: add support to query the attestation rep


From: James Bottomley
Subject: Re: [PATCH v2] target/i386/sev: add support to query the attestation report
Date: Tue, 05 Jan 2021 11:02:17 -0800
User-agent: Evolution 3.34.4

On Tue, 2021-01-05 at 10:39 -0600, Brijesh Singh wrote:
> The SEV FW >= 0.23 added a new command that can be used to query the
> attestation report containing the SHA-256 digest of the guest memory
> and VMSA encrypted with the LAUNCH_UPDATE and sign it with the PEK.
> 
> Note, we already have a command (LAUNCH_MEASURE) that can be used to
> query the SHA-256 digest of the guest memory encrypted through the
> LAUNCH_UPDATE. The main difference between previous and this command
> is that the report is signed with the PEK and unlike the
> LAUNCH_MEASURE
> command the ATTESATION_REPORT command can be called while the guest
> is running.
> 
> Add a QMP interface "query-sev-attestation-report" that can be used
> to get the report encoded in base64.
> 
> Cc: James Bottomley <jejb@linux.ibm.com>
> Cc: Tom Lendacky <Thomas.Lendacky@amd.com>
> Cc: Eric Blake <eblake@redhat.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: kvm@vger.kernel.org
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
> v2:
>   * add trace event.
>   * fix the goto to return NULL on failure.
>   * make the mnonce as a base64 encoded string

Yes, that fixes all my issues, thanks!

Reviewed-by: James Bottomley <jejb@linux.ibm.com>
Tested-by: James Bottomley <jejb@linux.ibm.com>

I've also attached a python script below which I've used to verify the
attestation.

James

---

#!/usr/bin/python3
##
# Python script get an attestation and verify it with the PEK
#
# This assumes you've already exported the pek.cert with sev-tool
# from https://github.com/AMDESE/sev-tool.git
#
# sev-tool --export_cert_chain
#
# creates several files, the only one this script needs is pek.cert
#
# Tables and chapters refer to the amd 55766.pdf document
#
# https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
##
import sys
import os 
import base64
import hashlib
from argparse import ArgumentParser
from Crypto.PublicKey import ECC
from Crypto.Math.Numbers import Integer
from git.qemu.python.qemu import qmp

if __name__ == "__main__":
    parser = ArgumentParser(description='Inject secret into SEV')
    parser.add_argument('--pek-cert',
                        help='The Platform DH certificate in binary form',
                        default='pek.cert')
    parser.add_argument('--socket',
                        help='Socket to connect to QMP on, defaults to 
localhost:6550',
                        default='localhost:6550')
    args = parser.parse_args()

    if (args.socket[0] == '/'):
        socket = args.socket
    elif (':' in args.socket):
        s = args.socket.split(':')
        socket = (s[0], int(s[1]))
    else:
        parse.error('--socket must be <host>:<port> or /path/to/unix')

    fh = open(args.pek_cert, 'rb')
    pek = bytearray(fh.read())
    curve = int.from_bytes(pek[16:20], byteorder='little')
    curves = {
        1: 'p256',
        2: 'p384'
        }
    Qx = int.from_bytes(bytes(pek[20:92]), byteorder='little')
    Qy = int.from_bytes(bytes(pek[92:164]), byteorder='little')

    pubkey = ECC.construct(point_x=Qx, point_y=Qy, curve=curves[curve])

    Qmp = qmp.QEMUMonitorProtocol(address=socket);
    Qmp.connect()
    caps = Qmp.command('query-sev')
    print('SEV query found API={api-major}.{api-minor} build={build-id} 
policy={policy}\n'.format(**caps))

    nonce=os.urandom(16)

    report = Qmp.command('query-sev-attestation-report',
                         mnonce=base64.b64encode(nonce).decode())

    a = base64.b64decode(report['data'])

    ##
    # returned data is formulated as Table 60. Attestation Report Buffer
    ##
    rnonce = a[0:16]
    rmeas = a[16:48]

    if (nonce != rnonce):
        sys.exit('returned nonce doesn\'t match input nonce')

    policy = int.from_bytes(a[48:52], byteorder='little')
    usage = int.from_bytes(a[52:56], byteorder='little')
    algo = int.from_bytes(a[56:60], byteorder='little')

    if (policy != caps['policy']):
        sys.exit('Policy mismatch:', policy, '!=', caps['policy'])

    if (usage != 0x1002):
        sys.exit('error PEK is not specified in usage: ', usage)

    if (algo == 0x2):
        h = hashlib.sha256()
    elif (algo == 0x102):
        ##
        # The spec (6.8) says the signature must be ECDSA-SHA256 so this
        # should be impossible, but it turns out to be the way our
        # current test hardware produces its signature
        ##
        h = hashlib.sha384()
    else:
        sys.exit('unrecognized signing algorithm: ', algo)

    h.update(a[0:52])

    sig = a[64:208]
    r = int.from_bytes(sig[0:72],byteorder='little')
    s = int.from_bytes(sig[72:144],byteorder='little')
    ##
    # subtlety: r and s are little (AMD defined) z is big (crypto requirement)
    ##
    z = int.from_bytes(h.digest(), byteorder='big')

    ##
    # python crypto doesn't have a way of passing in r and s as
    # integers and I'm not inclined to wrap them up as a big endian
    # binary signature to have Signature.DSS unwrap them again, so
    # call the _verify() private interface that does take integers
    ##
    if (not pubkey._verify(Integer(z), (Integer(r), Integer(s)))):
        sys.exit('returned signature did not verify')

    print('usage={usage}, algorithm={algo}'.format(usage=hex(usage),
                                                   algo=hex(algo)))
    print('ovmf-hash: ', rmeas.hex())




reply via email to

[Prev in Thread] Current Thread [Next in Thread]