[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())