diff mbox series

KVM/SVM: add support for SEV attestation command

Message ID 20201204212847.13256-1-brijesh.singh@amd.com
State Superseded
Headers show
Series KVM/SVM: add support for SEV attestation command | expand

Commit Message

Brijesh Singh Dec. 4, 2020, 9:28 p.m. UTC
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(+)

Comments

Tom Lendacky Dec. 8, 2020, 4:43 p.m. UTC | #1
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(&params, (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, &params, 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)

>
Ard Biesheuvel Dec. 9, 2020, 7:51 a.m. UTC | #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(&params, (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, &params, 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

>
Brijesh Singh Dec. 10, 2020, 3:25 a.m. UTC | #3
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.
David Rientjes Dec. 10, 2020, 11:28 p.m. UTC | #4
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!
James Bottomley Dec. 13, 2020, 10:28 p.m. UTC | #5
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 mbox series

Patch

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(&params, (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, &params, 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)