Message ID | 20201204212847.13256-1-brijesh.singh@amd.com |
---|---|
State | Superseded |
Headers | show |
Series | KVM/SVM: add support for SEV attestation command | expand |
On 12/4/20 3:28 PM, Brijesh Singh wrote: > The SEV FW version >= 0.23 added a new command that can be used to query > the attestation report containing the SHA-256 digest of the guest memory > encrypted through the KVM_SEV_LAUNCH_UPDATE_{DATA, VMSA} commands and > sign the report with the Platform Endorsement Key (PEK). > > See the SEV FW API spec section 6.8 for more details. > > Note there already exist a command (KVM_SEV_LAUNCH_MEASURE) that can be > used to get the SHA-256 digest. The main difference between the > KVM_SEV_LAUNCH_MEASURE and KVM_SEV_ATTESTATION_REPORT is that the later > can be called while the guest is running and the measurement value is > signed with PEK. > > Cc: James Bottomley <jejb@linux.ibm.com> > Cc: Tom Lendacky <Thomas.Lendacky@amd.com> > Cc: David Rientjes <rientjes@google.com> > Cc: Paolo Bonzini <pbonzini@redhat.com> > Cc: Sean Christopherson <seanjc@google.com> > Cc: Borislav Petkov <bp@alien8.de> > Cc: John Allen <john.allen@amd.com> > Cc: Herbert Xu <herbert@gondor.apana.org.au> > Cc: linux-crypto@vger.kernel.org > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com> Not sure if Paolo or Herbert would like the crypto/psp changes to be split out from the kvm changes as separate patches or not. Thanks, Tom > --- > .../virt/kvm/amd-memory-encryption.rst | 21 ++++++ > arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++ > drivers/crypto/ccp/sev-dev.c | 1 + > include/linux/psp-sev.h | 17 +++++ > include/uapi/linux/kvm.h | 8 +++ > 5 files changed, 118 insertions(+) > > diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst > index 09a8f2a34e39..4c6685d0fddd 100644 > --- a/Documentation/virt/kvm/amd-memory-encryption.rst > +++ b/Documentation/virt/kvm/amd-memory-encryption.rst > @@ -263,6 +263,27 @@ Returns: 0 on success, -negative on error > __u32 trans_len; > }; > > +10. KVM_SEV_GET_ATTESATION_REPORT > +--------------------------------- > + > +The KVM_SEV_GET_ATTESATION_REPORT command can be used by the hypervisor to query the attestation > +report containing the SHA-256 digest of the guest memory and VMSA passed through the KVM_SEV_LAUNCH > +commands and signed with the PEK. The digest returned by the command should match the digest > +used by the guest owner with the KVM_SEV_LAUNCH_MEASURE. > + > +Parameters (in): struct kvm_sev_attestation > + > +Returns: 0 on success, -negative on error > + > +:: > + > + struct kvm_sev_attestation_report { > + __u8 mnonce[16]; /* A random mnonce that will be placed in the report */ > + > + __u64 uaddr; /* userspace address where the report should be copied */ > + __u32 len; > + }; > + > References > ========== > > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c > index 566f4d18185b..c4d3ee6be362 100644 > --- a/arch/x86/kvm/svm/sev.c > +++ b/arch/x86/kvm/svm/sev.c > @@ -927,6 +927,74 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) > return ret; > } > > +static int sev_get_attestation_report(struct kvm *kvm, struct kvm_sev_cmd *argp) > +{ > + void __user *report = (void __user *)(uintptr_t)argp->data; > + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; > + struct sev_data_attestation_report *data; > + struct kvm_sev_attestation_report params; > + void __user *p; > + void *blob = NULL; > + int ret; > + > + if (!sev_guest(kvm)) > + return -ENOTTY; > + > + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) > + return -EFAULT; > + > + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); > + if (!data) > + return -ENOMEM; > + > + /* User wants to query the blob length */ > + if (!params.len) > + goto cmd; > + > + p = (void __user *)(uintptr_t)params.uaddr; > + if (p) { > + if (params.len > SEV_FW_BLOB_MAX_SIZE) { > + ret = -EINVAL; > + goto e_free; > + } > + > + ret = -ENOMEM; > + blob = kmalloc(params.len, GFP_KERNEL); > + if (!blob) > + goto e_free; > + > + data->address = __psp_pa(blob); > + data->len = params.len; > + memcpy(data->mnonce, params.mnonce, sizeof(params.mnonce)); > + } > +cmd: > + data->handle = sev->handle; > + ret = sev_issue_cmd(kvm, SEV_CMD_ATTESTATION_REPORT, data, &argp->error); > + /* > + * If we query the session length, FW responded with expected data. > + */ > + if (!params.len) > + goto done; > + > + if (ret) > + goto e_free_blob; > + > + if (blob) { > + if (copy_to_user(p, blob, params.len)) > + ret = -EFAULT; > + } > + > +done: > + params.len = data->len; > + if (copy_to_user(report, ¶ms, sizeof(params))) > + ret = -EFAULT; > +e_free_blob: > + kfree(blob); > +e_free: > + kfree(data); > + return ret; > +} > + > int svm_mem_enc_op(struct kvm *kvm, void __user *argp) > { > struct kvm_sev_cmd sev_cmd; > @@ -971,6 +1039,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp) > case KVM_SEV_LAUNCH_SECRET: > r = sev_launch_secret(kvm, &sev_cmd); > break; > + case KVM_SEV_GET_ATTESTATION_REPORT: > + r = sev_get_attestation_report(kvm, &sev_cmd); > + break; > default: > r = -EINVAL; > goto out; > diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c > index 476113e12489..cb9b4c4e371e 100644 > --- a/drivers/crypto/ccp/sev-dev.c > +++ b/drivers/crypto/ccp/sev-dev.c > @@ -128,6 +128,7 @@ static int sev_cmd_buffer_len(int cmd) > case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret); > case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware); > case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); > + case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report); > default: return 0; > } > > diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h > index 49d155cd2dfe..b801ead1e2bb 100644 > --- a/include/linux/psp-sev.h > +++ b/include/linux/psp-sev.h > @@ -66,6 +66,7 @@ enum sev_cmd { > SEV_CMD_LAUNCH_MEASURE = 0x033, > SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034, > SEV_CMD_LAUNCH_FINISH = 0x035, > + SEV_CMD_ATTESTATION_REPORT = 0x036, > > /* Guest migration commands (outgoing) */ > SEV_CMD_SEND_START = 0x040, > @@ -483,6 +484,22 @@ struct sev_data_dbg { > u32 len; /* In */ > } __packed; > > +/** > + * struct sev_data_attestation_report - SEV_ATTESTATION_REPORT command parameters > + * > + * @handle: handle of the VM > + * @mnonce: a random nonce that will be included in the report. > + * @address: physical address where the report will be copied. > + * @len: length of the physical buffer. > + */ > +struct sev_data_attestation_report { > + u32 handle; /* In */ > + u32 reserved; > + u64 address; /* In */ > + u8 mnonce[16]; /* In */ > + u32 len; /* In/Out */ > +} __packed; > + > #ifdef CONFIG_CRYPTO_DEV_SP_PSP > > /** > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > index ca41220b40b8..d3385f7f08a2 100644 > --- a/include/uapi/linux/kvm.h > +++ b/include/uapi/linux/kvm.h > @@ -1585,6 +1585,8 @@ enum sev_cmd_id { > KVM_SEV_DBG_ENCRYPT, > /* Guest certificates commands */ > KVM_SEV_CERT_EXPORT, > + /* Attestation report */ > + KVM_SEV_GET_ATTESTATION_REPORT, > > KVM_SEV_NR_MAX, > }; > @@ -1637,6 +1639,12 @@ struct kvm_sev_dbg { > __u32 len; > }; > > +struct kvm_sev_attestation_report { > + __u8 mnonce[16]; > + __u64 uaddr; > + __u32 len; > +}; > + > #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) > #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) > #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) >
On Fri, 4 Dec 2020 at 22:30, Brijesh Singh <brijesh.singh@amd.com> wrote: > > The SEV FW version >= 0.23 added a new command that can be used to query > the attestation report containing the SHA-256 digest of the guest memory > encrypted through the KVM_SEV_LAUNCH_UPDATE_{DATA, VMSA} commands and > sign the report with the Platform Endorsement Key (PEK). > > See the SEV FW API spec section 6.8 for more details. > > Note there already exist a command (KVM_SEV_LAUNCH_MEASURE) that can be > used to get the SHA-256 digest. The main difference between the > KVM_SEV_LAUNCH_MEASURE and KVM_SEV_ATTESTATION_REPORT is that the later latter > can be called while the guest is running and the measurement value is > signed with PEK. > > Cc: James Bottomley <jejb@linux.ibm.com> > Cc: Tom Lendacky <Thomas.Lendacky@amd.com> > Cc: David Rientjes <rientjes@google.com> > Cc: Paolo Bonzini <pbonzini@redhat.com> > Cc: Sean Christopherson <seanjc@google.com> > Cc: Borislav Petkov <bp@alien8.de> > Cc: John Allen <john.allen@amd.com> > Cc: Herbert Xu <herbert@gondor.apana.org.au> > Cc: linux-crypto@vger.kernel.org > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> > --- > .../virt/kvm/amd-memory-encryption.rst | 21 ++++++ > arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++ > drivers/crypto/ccp/sev-dev.c | 1 + > include/linux/psp-sev.h | 17 +++++ > include/uapi/linux/kvm.h | 8 +++ > 5 files changed, 118 insertions(+) > > diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst > index 09a8f2a34e39..4c6685d0fddd 100644 > --- a/Documentation/virt/kvm/amd-memory-encryption.rst > +++ b/Documentation/virt/kvm/amd-memory-encryption.rst > @@ -263,6 +263,27 @@ Returns: 0 on success, -negative on error > __u32 trans_len; > }; > > +10. KVM_SEV_GET_ATTESATION_REPORT KVM_SEV_GET_ATTESTATION_REPORT > +--------------------------------- > + > +The KVM_SEV_GET_ATTESATION_REPORT command can be used by the hypervisor to query the attestation KVM_SEV_GET_ATTESTATION_REPORT > +report containing the SHA-256 digest of the guest memory and VMSA passed through the KVM_SEV_LAUNCH > +commands and signed with the PEK. The digest returned by the command should match the digest > +used by the guest owner with the KVM_SEV_LAUNCH_MEASURE. > + > +Parameters (in): struct kvm_sev_attestation > + > +Returns: 0 on success, -negative on error > + > +:: > + > + struct kvm_sev_attestation_report { > + __u8 mnonce[16]; /* A random mnonce that will be placed in the report */ > + > + __u64 uaddr; /* userspace address where the report should be copied */ > + __u32 len; > + }; > + > References > ========== > > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c > index 566f4d18185b..c4d3ee6be362 100644 > --- a/arch/x86/kvm/svm/sev.c > +++ b/arch/x86/kvm/svm/sev.c > @@ -927,6 +927,74 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) > return ret; > } > > +static int sev_get_attestation_report(struct kvm *kvm, struct kvm_sev_cmd *argp) > +{ > + void __user *report = (void __user *)(uintptr_t)argp->data; > + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; > + struct sev_data_attestation_report *data; > + struct kvm_sev_attestation_report params; > + void __user *p; > + void *blob = NULL; > + int ret; > + > + if (!sev_guest(kvm)) > + return -ENOTTY; > + > + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) > + return -EFAULT; > + > + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); > + if (!data) > + return -ENOMEM; > + > + /* User wants to query the blob length */ > + if (!params.len) > + goto cmd; > + > + p = (void __user *)(uintptr_t)params.uaddr; > + if (p) { > + if (params.len > SEV_FW_BLOB_MAX_SIZE) { > + ret = -EINVAL; > + goto e_free; > + } > + > + ret = -ENOMEM; > + blob = kmalloc(params.len, GFP_KERNEL); > + if (!blob) > + goto e_free; > + > + data->address = __psp_pa(blob); > + data->len = params.len; > + memcpy(data->mnonce, params.mnonce, sizeof(params.mnonce)); > + } > +cmd: > + data->handle = sev->handle; > + ret = sev_issue_cmd(kvm, SEV_CMD_ATTESTATION_REPORT, data, &argp->error); > + /* > + * If we query the session length, FW responded with expected data. > + */ > + if (!params.len) > + goto done; > + > + if (ret) > + goto e_free_blob; > + > + if (blob) { > + if (copy_to_user(p, blob, params.len)) > + ret = -EFAULT; > + } > + > +done: > + params.len = data->len; > + if (copy_to_user(report, ¶ms, sizeof(params))) > + ret = -EFAULT; > +e_free_blob: > + kfree(blob); > +e_free: > + kfree(data); > + return ret; > +} > + > int svm_mem_enc_op(struct kvm *kvm, void __user *argp) > { > struct kvm_sev_cmd sev_cmd; > @@ -971,6 +1039,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp) > case KVM_SEV_LAUNCH_SECRET: > r = sev_launch_secret(kvm, &sev_cmd); > break; > + case KVM_SEV_GET_ATTESTATION_REPORT: > + r = sev_get_attestation_report(kvm, &sev_cmd); > + break; > default: > r = -EINVAL; > goto out; > diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c > index 476113e12489..cb9b4c4e371e 100644 > --- a/drivers/crypto/ccp/sev-dev.c > +++ b/drivers/crypto/ccp/sev-dev.c > @@ -128,6 +128,7 @@ static int sev_cmd_buffer_len(int cmd) > case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret); > case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware); > case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); > + case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report); > default: return 0; > } > > diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h > index 49d155cd2dfe..b801ead1e2bb 100644 > --- a/include/linux/psp-sev.h > +++ b/include/linux/psp-sev.h > @@ -66,6 +66,7 @@ enum sev_cmd { > SEV_CMD_LAUNCH_MEASURE = 0x033, > SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034, > SEV_CMD_LAUNCH_FINISH = 0x035, > + SEV_CMD_ATTESTATION_REPORT = 0x036, > > /* Guest migration commands (outgoing) */ > SEV_CMD_SEND_START = 0x040, > @@ -483,6 +484,22 @@ struct sev_data_dbg { > u32 len; /* In */ > } __packed; > > +/** > + * struct sev_data_attestation_report - SEV_ATTESTATION_REPORT command parameters > + * > + * @handle: handle of the VM > + * @mnonce: a random nonce that will be included in the report. > + * @address: physical address where the report will be copied. > + * @len: length of the physical buffer. > + */ > +struct sev_data_attestation_report { > + u32 handle; /* In */ > + u32 reserved; > + u64 address; /* In */ > + u8 mnonce[16]; /* In */ > + u32 len; /* In/Out */ > +} __packed; > + > #ifdef CONFIG_CRYPTO_DEV_SP_PSP > > /** > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > index ca41220b40b8..d3385f7f08a2 100644 > --- a/include/uapi/linux/kvm.h > +++ b/include/uapi/linux/kvm.h > @@ -1585,6 +1585,8 @@ enum sev_cmd_id { > KVM_SEV_DBG_ENCRYPT, > /* Guest certificates commands */ > KVM_SEV_CERT_EXPORT, > + /* Attestation report */ > + KVM_SEV_GET_ATTESTATION_REPORT, > > KVM_SEV_NR_MAX, > }; > @@ -1637,6 +1639,12 @@ struct kvm_sev_dbg { > __u32 len; > }; > > +struct kvm_sev_attestation_report { > + __u8 mnonce[16]; > + __u64 uaddr; > + __u32 len; > +}; > + > #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) > #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) > #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) > -- > 2.17.1 >
On 12/9/20 1:51 AM, Ard Biesheuvel wrote: > On Fri, 4 Dec 2020 at 22:30, Brijesh Singh <brijesh.singh@amd.com> wrote: >> The SEV FW version >= 0.23 added a new command that can be used to query >> the attestation report containing the SHA-256 digest of the guest memory >> encrypted through the KVM_SEV_LAUNCH_UPDATE_{DATA, VMSA} commands and >> sign the report with the Platform Endorsement Key (PEK). >> >> See the SEV FW API spec section 6.8 for more details. >> >> Note there already exist a command (KVM_SEV_LAUNCH_MEASURE) that can be >> used to get the SHA-256 digest. The main difference between the >> KVM_SEV_LAUNCH_MEASURE and KVM_SEV_ATTESTATION_REPORT is that the later > latter > >> can be called while the guest is running and the measurement value is >> signed with PEK. >> >> Cc: James Bottomley <jejb@linux.ibm.com> >> Cc: Tom Lendacky <Thomas.Lendacky@amd.com> >> Cc: David Rientjes <rientjes@google.com> >> Cc: Paolo Bonzini <pbonzini@redhat.com> >> Cc: Sean Christopherson <seanjc@google.com> >> Cc: Borislav Petkov <bp@alien8.de> >> Cc: John Allen <john.allen@amd.com> >> Cc: Herbert Xu <herbert@gondor.apana.org.au> >> Cc: linux-crypto@vger.kernel.org >> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> >> --- >> .../virt/kvm/amd-memory-encryption.rst | 21 ++++++ >> arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++ >> drivers/crypto/ccp/sev-dev.c | 1 + >> include/linux/psp-sev.h | 17 +++++ >> include/uapi/linux/kvm.h | 8 +++ >> 5 files changed, 118 insertions(+) >> >> diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst >> index 09a8f2a34e39..4c6685d0fddd 100644 >> --- a/Documentation/virt/kvm/amd-memory-encryption.rst >> +++ b/Documentation/virt/kvm/amd-memory-encryption.rst >> @@ -263,6 +263,27 @@ Returns: 0 on success, -negative on error >> __u32 trans_len; >> }; >> >> +10. KVM_SEV_GET_ATTESATION_REPORT > KVM_SEV_GET_ATTESTATION_REPORT > >> +--------------------------------- >> + >> +The KVM_SEV_GET_ATTESATION_REPORT command can be used by the hypervisor to query the attestation > KVM_SEV_GET_ATTESTATION_REPORT Noted, I will send v2 with these fixed.
On Wed, 9 Dec 2020, Brijesh Singh wrote: > Noted, I will send v2 with these fixed. > And with those changes: Acked-by: David Rientjes <rientjes@google.com> Thanks Brijesh!
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@linux.ibm.com> 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())
diff --git a/Documentation/virt/kvm/amd-memory-encryption.rst b/Documentation/virt/kvm/amd-memory-encryption.rst index 09a8f2a34e39..4c6685d0fddd 100644 --- a/Documentation/virt/kvm/amd-memory-encryption.rst +++ b/Documentation/virt/kvm/amd-memory-encryption.rst @@ -263,6 +263,27 @@ Returns: 0 on success, -negative on error __u32 trans_len; }; +10. KVM_SEV_GET_ATTESATION_REPORT +--------------------------------- + +The KVM_SEV_GET_ATTESATION_REPORT command can be used by the hypervisor to query the attestation +report containing the SHA-256 digest of the guest memory and VMSA passed through the KVM_SEV_LAUNCH +commands and signed with the PEK. The digest returned by the command should match the digest +used by the guest owner with the KVM_SEV_LAUNCH_MEASURE. + +Parameters (in): struct kvm_sev_attestation + +Returns: 0 on success, -negative on error + +:: + + struct kvm_sev_attestation_report { + __u8 mnonce[16]; /* A random mnonce that will be placed in the report */ + + __u64 uaddr; /* userspace address where the report should be copied */ + __u32 len; + }; + References ========== diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 566f4d18185b..c4d3ee6be362 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -927,6 +927,74 @@ static int sev_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; } +static int sev_get_attestation_report(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + void __user *report = (void __user *)(uintptr_t)argp->data; + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; + struct sev_data_attestation_report *data; + struct kvm_sev_attestation_report params; + void __user *p; + void *blob = NULL; + int ret; + + if (!sev_guest(kvm)) + return -ENOTTY; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) + return -EFAULT; + + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); + if (!data) + return -ENOMEM; + + /* User wants to query the blob length */ + if (!params.len) + goto cmd; + + p = (void __user *)(uintptr_t)params.uaddr; + if (p) { + if (params.len > SEV_FW_BLOB_MAX_SIZE) { + ret = -EINVAL; + goto e_free; + } + + ret = -ENOMEM; + blob = kmalloc(params.len, GFP_KERNEL); + if (!blob) + goto e_free; + + data->address = __psp_pa(blob); + data->len = params.len; + memcpy(data->mnonce, params.mnonce, sizeof(params.mnonce)); + } +cmd: + data->handle = sev->handle; + ret = sev_issue_cmd(kvm, SEV_CMD_ATTESTATION_REPORT, data, &argp->error); + /* + * If we query the session length, FW responded with expected data. + */ + if (!params.len) + goto done; + + if (ret) + goto e_free_blob; + + if (blob) { + if (copy_to_user(p, blob, params.len)) + ret = -EFAULT; + } + +done: + params.len = data->len; + if (copy_to_user(report, ¶ms, sizeof(params))) + ret = -EFAULT; +e_free_blob: + kfree(blob); +e_free: + kfree(data); + return ret; +} + int svm_mem_enc_op(struct kvm *kvm, void __user *argp) { struct kvm_sev_cmd sev_cmd; @@ -971,6 +1039,9 @@ int svm_mem_enc_op(struct kvm *kvm, void __user *argp) case KVM_SEV_LAUNCH_SECRET: r = sev_launch_secret(kvm, &sev_cmd); break; + case KVM_SEV_GET_ATTESTATION_REPORT: + r = sev_get_attestation_report(kvm, &sev_cmd); + break; default: r = -EINVAL; goto out; diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 476113e12489..cb9b4c4e371e 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -128,6 +128,7 @@ static int sev_cmd_buffer_len(int cmd) case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret); case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware); case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); + case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report); default: return 0; } diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index 49d155cd2dfe..b801ead1e2bb 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -66,6 +66,7 @@ enum sev_cmd { SEV_CMD_LAUNCH_MEASURE = 0x033, SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034, SEV_CMD_LAUNCH_FINISH = 0x035, + SEV_CMD_ATTESTATION_REPORT = 0x036, /* Guest migration commands (outgoing) */ SEV_CMD_SEND_START = 0x040, @@ -483,6 +484,22 @@ struct sev_data_dbg { u32 len; /* In */ } __packed; +/** + * struct sev_data_attestation_report - SEV_ATTESTATION_REPORT command parameters + * + * @handle: handle of the VM + * @mnonce: a random nonce that will be included in the report. + * @address: physical address where the report will be copied. + * @len: length of the physical buffer. + */ +struct sev_data_attestation_report { + u32 handle; /* In */ + u32 reserved; + u64 address; /* In */ + u8 mnonce[16]; /* In */ + u32 len; /* In/Out */ +} __packed; + #ifdef CONFIG_CRYPTO_DEV_SP_PSP /** diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index ca41220b40b8..d3385f7f08a2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1585,6 +1585,8 @@ enum sev_cmd_id { KVM_SEV_DBG_ENCRYPT, /* Guest certificates commands */ KVM_SEV_CERT_EXPORT, + /* Attestation report */ + KVM_SEV_GET_ATTESTATION_REPORT, KVM_SEV_NR_MAX, }; @@ -1637,6 +1639,12 @@ struct kvm_sev_dbg { __u32 len; }; +struct kvm_sev_attestation_report { + __u8 mnonce[16]; + __u64 uaddr; + __u32 len; +}; + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)
The SEV FW version >= 0.23 added a new command that can be used to query the attestation report containing the SHA-256 digest of the guest memory encrypted through the KVM_SEV_LAUNCH_UPDATE_{DATA, VMSA} commands and sign the report with the Platform Endorsement Key (PEK). See the SEV FW API spec section 6.8 for more details. Note there already exist a command (KVM_SEV_LAUNCH_MEASURE) that can be used to get the SHA-256 digest. The main difference between the KVM_SEV_LAUNCH_MEASURE and KVM_SEV_ATTESTATION_REPORT is that the later can be called while the guest is running and the measurement value is signed with PEK. Cc: James Bottomley <jejb@linux.ibm.com> Cc: Tom Lendacky <Thomas.Lendacky@amd.com> Cc: David Rientjes <rientjes@google.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Sean Christopherson <seanjc@google.com> Cc: Borislav Petkov <bp@alien8.de> Cc: John Allen <john.allen@amd.com> Cc: Herbert Xu <herbert@gondor.apana.org.au> Cc: linux-crypto@vger.kernel.org Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> --- .../virt/kvm/amd-memory-encryption.rst | 21 ++++++ arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++ drivers/crypto/ccp/sev-dev.c | 1 + include/linux/psp-sev.h | 17 +++++ include/uapi/linux/kvm.h | 8 +++ 5 files changed, 118 insertions(+)