Skip to content

Commit

Permalink
Add support for SEV attestion in 6.x kernels (#5848)
Browse files Browse the repository at this point in the history
  • Loading branch information
achamayou authored Dec 4, 2023
1 parent 6d4d6bc commit 1b9be61
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .azure-pipelines-templates/deploy_aci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
- script: |
set -ex
docker login -u $ACR_TOKEN_NAME -p $ACR_CI_PUSH_TOKEN_PASSWORD $ACR_REGISTRY
docker pull $ACR_REGISTRY/ccf/ci:05-09-2023-snp-clang15
docker pull $ACR_REGISTRY/ccf/ci:26-10-2023-snp-clang15
docker build -f docker/ccf_ci_built . --build-arg="base=$BASE_IMAGE" --build-arg="platform=snp" -t $ACR_REGISTRY/ccf/ci:pr-`git rev-parse HEAD`
docker push $ACR_REGISTRY/ccf/ci:pr-`git rev-parse HEAD`
name: build_ci_image
Expand Down
2 changes: 1 addition & 1 deletion .snpcc_canary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
___ ___ ___
(. =) Y (0 0) (x X) Y
O \ o | /
/-xXx--//-----x=x--/-xXx--/---x---->>
/-xXx--//-----x=x--/-xXx--/---x---->>|
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [5.0.0-dev8]

[5.0.0-dev8]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev8

NOTE: UNRELEASED

- `ccf.crypto.generateEddsaKeyPair`, `pubEddsaPemToJwk` and `eddsaPemToJwk` now support `x25519` as well as `curve25519` (#5846).

[5.0.0-dev8]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev8

- `POST /recovery/members/{memberId}:recover` is now authenticated by COSE Sign1, making it consistent with the other `POST` endpoints in governance, and avoiding a potential denial of service where un-authenticated and un-authorised clients could submit invalid shares repeatedly. The `submit_recovery_share.sh` script has been amended accordingly, and now takes a `--member-id-privk` and `--member-id-cert` (#5821).
- CCF can now fetch SEV-SNP attestations from kernel 6.0 and above (#5848).

## [5.0.0-dev7]

Expand Down
53 changes: 5 additions & 48 deletions include/ccf/pal/attestation.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
#include "ccf/ds/hex.h"
#include "ccf/ds/logger.h"
#include "ccf/ds/quote_info.h"
#include "ccf/pal/attestation_sev_snp.h"
#include "ccf/pal/measurement.h"
#include "ccf/pal/snp_ioctl.h"

#include <fcntl.h>
#include <functional>
#include <unistd.h>

#if !defined(INSIDE_ENCLAVE) || defined(VIRTUAL_ENCLAVE)
# include <sys/ioctl.h>
Expand Down Expand Up @@ -216,59 +215,17 @@ namespace ccf::pal
const snp::EndorsementsServers& endorsements_servers = {})
{
QuoteInfo node_quote_info = {};

node_quote_info.format = QuoteFormat::amd_sev_snp_v1;
int fd = open(snp::DEVICE, O_RDWR | O_CLOEXEC);
if (fd < 0)
{
throw std::logic_error(fmt::format("Failed to open \"{}\"", snp::DEVICE));
}

snp::AttestationReq req = {};
snp::AttestationResp resp = {};

// Arbitrary report data
if (report_data.data.size() <= snp_attestation_report_data_size)
{
std::copy(
report_data.data.begin(), report_data.data.end(), req.report_data);
}
else
{
throw std::logic_error(
"User-defined report data is larger than available space");
}

// Documented at
// https://www.kernel.org/doc/html/latest/virt/coco/sev-guest.html
snp::GuestRequest payload = {
.req_msg_type = snp::MSG_REPORT_REQ,
.rsp_msg_type = snp::MSG_REPORT_RSP,
.msg_version = 1,
.request_len = sizeof(req),
.request_uaddr = reinterpret_cast<uint64_t>(&req),
.response_len = sizeof(resp),
.response_uaddr = reinterpret_cast<uint64_t>(&resp),
.error = 0};

int rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload);
if (rc < 0)
{
CCF_APP_FAIL("IOCTL call failed: {}", strerror(errno));
CCF_APP_FAIL("Payload error: {}", payload.error);
throw std::logic_error("Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT");
}
auto attestation = snp::get_attestation(report_data);

auto quote = &resp.report;
auto quote_bytes = reinterpret_cast<uint8_t*>(&resp.report);
node_quote_info.quote.assign(quote_bytes, quote_bytes + resp.report_size);
node_quote_info.quote = attestation->get_raw();

if (endorsement_cb != nullptr)
{
endorsement_cb(
node_quote_info,
snp::make_endorsement_endpoint_configuration(
*quote, endorsements_servers));
attestation->get(), endorsements_servers));
}
}
#endif
Expand All @@ -280,7 +237,7 @@ namespace ccf::pal
PlatformAttestationMeasurement& measurement,
PlatformAttestationReportData& report_data)
{
auto is_sev_snp = access(snp::DEVICE, F_OK) == 0;
auto is_sev_snp = snp::is_sev_snp();

if (quote_info.format == QuoteFormat::insecure_virtual)
{
Expand Down
67 changes: 7 additions & 60 deletions include/ccf/pal/attestation_sev_snp.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,63 +157,6 @@ QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==
};
#pragma pack(pop)

// Table 20
struct AttestationReq
{
uint8_t report_data[snp_attestation_report_data_size];
uint32_t vmpl;
uint8_t reserved[28];
};

// Table 23
#pragma pack(push, 1)
struct AttestationResp
{
uint32_t status;
uint32_t report_size;
uint8_t reserved[0x20 - 0x8];
struct Attestation report;
uint8_t padding[64];
// padding to the size of SEV_SNP_REPORT_RSP_BUF_SZ (i.e., 1280 bytes)
};
#pragma pack(pop)

struct GuestRequest
{
uint8_t req_msg_type;
uint8_t rsp_msg_type;
uint8_t msg_version;
uint16_t request_len;
uint64_t request_uaddr;
uint16_t response_len;
uint64_t response_uaddr;
uint32_t error; /* firmware error code on failure (see psp-sev.h) */
};

// Table 99
enum MsgType
{
MSG_TYPE_INVALID = 0,
MSG_CPUID_REQ,
MSG_CPUID_RSP,
MSG_KEY_REQ,
MSG_KEY_RSP,
MSG_REPORT_REQ,
MSG_REPORT_RSP,
MSG_EXPORT_REQ,
MSG_EXPORT_RSP,
MSG_IMPORT_REQ,
MSG_IMPORT_RSP,
MSG_ABSORB_REQ,
MSG_ABSORB_RSP,
MSG_VMRK_REQ,
MSG_VMRK_RSP,
MSG_TYPE_MAX
};

// Changes on 5.19+ kernel
constexpr auto DEVICE = "/dev/sev";

static EndorsementEndpointsConfiguration
make_endorsement_endpoint_configuration(
const Attestation& quote,
Expand Down Expand Up @@ -268,8 +211,12 @@ QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==
return config;
}

#define SEV_GUEST_IOC_TYPE 'S'
#define SEV_SNP_GUEST_MSG_REPORT \
_IOWR(SEV_GUEST_IOC_TYPE, 0x1, struct snp::GuestRequest)
class AttestationInterface
{
public:
virtual const snp::Attestation& get() const = 0;
virtual std::vector<uint8_t> get_raw() = 0;

virtual ~AttestationInterface() = default;
};
}
31 changes: 31 additions & 0 deletions include/ccf/pal/snp_ioctl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once

#include "ccf/pal/snp_ioctl5.h"
#include "ccf/pal/snp_ioctl6.h"

namespace ccf::pal::snp
{
static inline bool is_sev_snp()
{
return ioctl5::is_sev_snp() || ioctl6::is_sev_snp();
}

static std::unique_ptr<AttestationInterface> get_attestation(
const PlatformAttestationReportData& report_data)
{
if (ioctl5::is_sev_snp())
{
return std::make_unique<ioctl5::Attestation>(report_data);
}
else if (ioctl6::is_sev_snp())
{
return std::make_unique<ioctl6::Attestation>(report_data);
}
else
{
throw std::logic_error("SEV-SNP not supported");
}
}
};
143 changes: 143 additions & 0 deletions include/ccf/pal/snp_ioctl5.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once

#include "ccf/pal/attestation_sev_snp.h"

#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>

// Based on the SEV-SNP ABI Spec document at
// https://www.amd.com/system/files/TechDocs/56860.pdf

/* linux kernel 5.15.* versions of the ioctls that talk to the PSP */

namespace ccf::pal::snp::ioctl5
{
constexpr auto DEVICE = "/dev/sev";

struct GuestRequest
{
uint8_t req_msg_type;
uint8_t rsp_msg_type;
uint8_t msg_version;
uint16_t request_len;
uint64_t request_uaddr;
uint16_t response_len;
uint64_t response_uaddr;
uint32_t error; /* firmware error code on failure (see psp-sev.h) */
};

// Table 99
enum MsgType
{
MSG_TYPE_INVALID = 0,
MSG_CPUID_REQ,
MSG_CPUID_RSP,
MSG_KEY_REQ,
MSG_KEY_RSP,
MSG_REPORT_REQ,
MSG_REPORT_RSP,
MSG_EXPORT_REQ,
MSG_EXPORT_RSP,
MSG_IMPORT_REQ,
MSG_IMPORT_RSP,
MSG_ABSORB_REQ,
MSG_ABSORB_RSP,
MSG_VMRK_REQ,
MSG_VMRK_RSP,
MSG_TYPE_MAX
};

// Table 20
struct AttestationReq
{
uint8_t report_data[snp_attestation_report_data_size];
uint32_t vmpl;
uint8_t reserved[28];
};

// Table 23
#pragma pack(push, 1)
struct AttestationResp
{
uint32_t status;
uint32_t report_size;
uint8_t reserved[0x20 - 0x8];
struct Attestation report;
uint8_t padding[64];
// padding to the size of SEV_SNP_REPORT_RSP_BUF_SZ (i.e., 1280 bytes)
};
#pragma pack(pop)

constexpr char SEV_GUEST_IOC_TYPE = 'S';
constexpr int SEV_SNP_GUEST_MSG_REPORT =
_IOWR(SEV_GUEST_IOC_TYPE, 0x1, struct snp::ioctl5::GuestRequest);

static inline bool is_sev_snp()
{
return access(DEVICE, W_OK) == 0;
}

class Attestation : public AttestationInterface
{
AttestationReq req = {};
AttestationResp resp = {};

public:
Attestation(const PlatformAttestationReportData& report_data)
{
if (report_data.data.size() <= snp_attestation_report_data_size)
{
std::copy(
report_data.data.begin(), report_data.data.end(), req.report_data);
}
else
{
throw std::logic_error(
"User-defined report data is larger than available space");
}

int fd = open(DEVICE, O_RDWR | O_CLOEXEC);
if (fd < 0)
{
throw std::logic_error(fmt::format("Failed to open \"{}\"", DEVICE));
}

// Documented at
// https://www.kernel.org/doc/html/latest/virt/coco/sev-guest.html
GuestRequest payload = {
.req_msg_type = MSG_REPORT_REQ,
.rsp_msg_type = MSG_REPORT_RSP,
.msg_version = 1,
.request_len = sizeof(req),
.request_uaddr = reinterpret_cast<uint64_t>(&req),
.response_len = sizeof(resp),
.response_uaddr = reinterpret_cast<uint64_t>(&resp),
.error = 0};

int rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload);
if (rc < 0)
{
CCF_APP_FAIL("IOCTL call failed: {}", strerror(errno));
CCF_APP_FAIL("Payload error: {}", payload.error);
throw std::logic_error(
"Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT");
}
}

const snp::Attestation& get() const override
{
return resp.report;
}

std::vector<uint8_t> get_raw() override
{
auto quote_bytes = reinterpret_cast<uint8_t*>(&resp.report);
return {quote_bytes, quote_bytes + resp.report_size};
}
};
}
Loading

0 comments on commit 1b9be61

Please sign in to comment.