On Wed, 2020-12-09 at 21:25 -0600, Brijesh Singh wrote: > Noted, I will send v2 with these fixed. I ran a test on this. It turns out for rome systems you need firmware md_sev_fam17h_model3xh_0.24b0A (or later) installed to get this and the QEMU patch with the base64 decoding fixed, but with that Tested-by: James Bottomley <jejb@xxxxxxxxxxxxx> Attached is the test programme I used. 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())