Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNP derived key #6791

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
8 changes: 4 additions & 4 deletions .snpcc_canary
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
___ ___ ___
(. =) Y (0 0) (x X) Y
O \ o | /
/-xXx--//-----x=x--/-xXx--/---x---->>>--/
___ ___ ___ \/
(. =) Y (0 0) (x X) Y (__)
O \ o | / |
/-xXx--//-----x=x--/-xXx--/---x-/--->>>--/
...
/\/\d(-_-)b/\/\
----vmpl---
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,13 @@ if(BUILD_TESTS)

# Unit tests
if(BUILD_UNIT_TESTS)
if(COMPILE_TARGET STREQUAL "snp")
add_unit_test(
snp_ioctl_test
${CMAKE_CURRENT_SOURCE_DIR}/src/pal/test/snp_ioctl_test.cpp
)
endif()

add_unit_test(map_test ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/map_test.cpp)

add_unit_test(
Expand Down
8 changes: 4 additions & 4 deletions include/ccf/crypto/symmetric_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ namespace ccf::crypto
/// @param aad Additional authenticated data
/// @return ciphertext
std::vector<uint8_t> aes_gcm_encrypt(
const std::vector<uint8_t>& key,
std::vector<uint8_t>& plaintext,
std::span<const uint8_t> key,
std::span<const uint8_t> plaintext,
const std::vector<uint8_t>& iv = default_iv,
const std::vector<uint8_t>& aad = {});

Expand All @@ -125,8 +125,8 @@ namespace ccf::crypto
/// @param aad Additional authenticated data
/// @return plaintext
std::vector<uint8_t> aes_gcm_decrypt(
const std::vector<uint8_t>& key,
std::vector<uint8_t>& ciphertext,
std::span<const uint8_t> key,
std::span<const uint8_t> ciphertext,
const std::vector<uint8_t>& iv = default_iv,
const std::vector<uint8_t>& aad = {});
}
12 changes: 12 additions & 0 deletions include/ccf/pal/snp_ioctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,16 @@ namespace ccf::pal::snp
throw std::logic_error("SEV-SNP not supported");
}
}

static std::unique_ptr<ioctl6::DerivedKey> make_derived_key()
{
if (ioctl6::is_sev_snp())
{
return std::make_unique<ioctl6::DerivedKey>();
}
else
{
throw std::logic_error("SEV-SNP Derived key not supported");
}
}
};
2 changes: 2 additions & 0 deletions include/ccf/pal/snp_ioctl5.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ namespace ccf::pal::snp::ioctl5
};

// Table 22
#pragma pack(push, 1)
struct AttestationReq
{
uint8_t report_data[snp_attestation_report_data_size];
uint32_t vmpl = 0;
uint8_t reserved[28];
};
#pragma pack(pop)

// Table 25
#pragma pack(push, 1)
Expand Down
132 changes: 118 additions & 14 deletions include/ccf/pal/snp_ioctl6.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ namespace ccf::pal::snp::ioctl6
constexpr auto DEVICE = "/dev/sev-guest";

// Table 22
#pragma pack(push, 1)
struct AttestationReq
{
uint8_t report_data[snp_attestation_report_data_size];
uint32_t vmpl = 0;
uint8_t reserved[28]; // needs to be zero
}; // aka snp_report_req in (linux) include/uapi/linux/sev-guest.h
}; // snp_report_req in (linux) include/uapi/linux/sev-guest.h
#pragma pack(pop)

// Table 25
#pragma pack(push, 1)
Expand All @@ -47,6 +49,56 @@ namespace ccf::pal::snp::ioctl6
};
#pragma pack(pop)

#pragma pack(push, 1)
// Table 20
struct DerivedKeyGuestFieldSelect
{
uint64_t reserved : 58;
uint32_t tcb_version : 1;
uint32_t guest_svn : 1;
uint32_t measurement : 1;
uint32_t family_id : 1;
uint32_t image_id : 1;
uint32_t guest_policy : 1;
};
static_assert(sizeof(DerivedKeyGuestFieldSelect) == 8);

// Table 19
struct KeySelect
{
uint32_t reserved : 29;
uint8_t key_sel : 2;
uint8_t root_key_sel : 1;
};
static_assert(sizeof(KeySelect) == 4);

struct DerivedKeyReq
{
KeySelect key_select;
uint32_t reserved;
DerivedKeyGuestFieldSelect guest_field_select;
uint32_t vmpl = 0;
uint32_t guest_svn;
uint64_t tcb_version;
}; // snp_derived_key_req in (linux) include/uapi/linux/sev-guest.h
#pragma pack(pop)

// Table 21
#pragma pack(push, 1)
struct DerivedKeyResp
{
uint32_t status;
uint8_t reserved[0x20 - 0x04];
uint8_t data[32];
}; // snp_derived_key_req in (linux) include/uapi/linux/sev-guest.h
static_assert(sizeof(DerivedKeyResp) < 4000);
struct DerivedKeyRespWrapper
{
struct DerivedKeyResp resp;
uint8_t padding[4000 - sizeof(struct DerivedKeyResp)];
achamayou marked this conversation as resolved.
Show resolved Hide resolved
};
#pragma pack(pop)

struct ExitInfoErrors
{
uint32_t fw;
Expand All @@ -60,23 +112,31 @@ namespace ccf::pal::snp::ioctl6
};

// https://www.kernel.org/doc/html/v6.4/virt/coco/sev-guest.html#api-description
template <typename Req, typename Resp>
struct GuestRequest
{
/* Message version number */
uint32_t msg_version;
uint32_t msg_version = 1;

/* Request and response structure address */
AttestationReq* req_data;
AttestationRespWrapper* resp_wrapper;
Req* req_data;
Resp* resp_wrapper;

/* bits[63:32]: VMM error code, bits[31:0] firmware error code (see
* psp-sev.h) */
ExitInfo exit_info;
};
using GuestRequestAttestation =
GuestRequest<AttestationReq, AttestationRespWrapper>;
using GuestRequestDerivedKey =
GuestRequest<DerivedKeyReq, DerivedKeyRespWrapper>;

// From linux/include/uapi/linux/sev-guest.h
constexpr char SEV_GUEST_IOC_TYPE = 'S';
constexpr int SEV_SNP_GUEST_MSG_REPORT =
_IOWR(SEV_GUEST_IOC_TYPE, 0x0, struct snp::ioctl6::GuestRequest);
_IOWR(SEV_GUEST_IOC_TYPE, 0x0, GuestRequestAttestation);
constexpr int SEV_SNP_GUEST_MSG_DERIVED_KEY =
_IOWR(SEV_GUEST_IOC_TYPE, 0x1, GuestRequestDerivedKey);

static inline bool is_sev_snp()
{
Expand Down Expand Up @@ -105,22 +165,20 @@ namespace ccf::pal::snp::ioctl6
int fd = open(DEVICE, O_RDWR | O_CLOEXEC);
if (fd < 0)
{
throw std::logic_error(fmt::format("Failed to open \"{}\"", DEVICE));
throw std::logic_error(
fmt::format("Failed to open \"{}\" ({})", DEVICE, fd));
}

// Documented at
// https://www.kernel.org/doc/html/latest/virt/coco/sev-guest.html
GuestRequest payload = {
.msg_version = 1,
.req_data = &req,
.resp_wrapper = &resp_wrapper,
.exit_info = {0}};
GuestRequestAttestation payload = {
.req_data = &req, .resp_wrapper = &resp_wrapper, .exit_info = {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(
LOG_FAIL_FMT("IOCTL call failed: {}", strerror(errno));
LOG_FAIL_FMT(
"Exit info, fw_error: {} vmm_error: {}",
payload.exit_info.errors.fw,
payload.exit_info.errors.vmm);
Expand All @@ -140,4 +198,50 @@ namespace ccf::pal::snp::ioctl6
return {quote_bytes, quote_bytes + resp_wrapper.resp.report_size};
}
};
}

class DerivedKey
{
DerivedKeyRespWrapper resp_wrapper = {};

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

// This req by default mixes in HostData and the CPU VCEK
DerivedKeyReq req = {};
// We must also mix in the measurement
req.guest_field_select.measurement = 1;
achamayou marked this conversation as resolved.
Show resolved Hide resolved
GuestRequestDerivedKey payload = {
.req_data = &req, .resp_wrapper = &resp_wrapper, .exit_info = {0}};
int rc = ioctl(fd, SEV_SNP_GUEST_MSG_DERIVED_KEY, &payload);
if (rc < 0)
{
LOG_FAIL_FMT("IOCTL call failed: {}", strerror(errno));
LOG_FAIL_FMT(
"Exit info, fw_error: {} vmm_error: {}",
payload.exit_info.errors.fw,
payload.exit_info.errors.vmm);
throw std::logic_error(
"Failed to issue ioctl SEV_SNP_GUEST_MSG_DERIVED_KEY");
}
if ((*payload.resp_wrapper).resp.status != 0)
{
LOG_FAIL_FMT(
"SNP_GUEST_DERIVED_KEY failed: {}", resp_wrapper.resp.status);
throw std::logic_error(
"Failed to issue ioctl SEV_SNP_GUEST_MSG_DERIVED_KEY");
}
}
eddyashton marked this conversation as resolved.
Show resolved Hide resolved

const std::span<const uint8_t> get_raw()
cjen1-msft marked this conversation as resolved.
Show resolved Hide resolved
{
return std::span<const uint8_t>{resp_wrapper.resp.data};
}
};
}
8 changes: 4 additions & 4 deletions src/crypto/symmetric_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ namespace ccf::crypto
}

std::vector<uint8_t> aes_gcm_encrypt(
const std::vector<uint8_t>& key,
std::vector<uint8_t>& plaintext,
std::span<const uint8_t> key,
std::span<const uint8_t> plaintext,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& aad)
{
Expand All @@ -116,8 +116,8 @@ namespace ccf::crypto
}

std::vector<uint8_t> aes_gcm_decrypt(
const std::vector<uint8_t>& key,
std::vector<uint8_t>& ciphertext,
std::span<const uint8_t> key,
std::span<const uint8_t> ciphertext,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& aad)
{
Expand Down
40 changes: 40 additions & 0 deletions src/pal/test/snp_ioctl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.

#include "ccf/crypto/symmetric_key.h"
#include "ccf/ds/logger.h"
#include "ccf/pal/snp_ioctl.h"
#include "crypto/openssl/hash.h"

#include <random>
#include <span>
#include <string>

#define DOCTEST_CONFIG_IMPLEMENT
#include <doctest/doctest.h>

TEST_CASE("SNP derive key")
{
using namespace ccf::pal;
auto key1 = snp::make_derived_key();
auto key2 = snp::make_derived_key();
cjen1-msft marked this conversation as resolved.
Show resolved Hide resolved

std::vector<uint8_t> expected_plaintext = {0xde, 0xad, 0xbe, 0xef};
auto ciphertext =
ccf::crypto::aes_gcm_encrypt(key1->get_raw(), expected_plaintext);
auto decrypted_plaintext =
ccf::crypto::aes_gcm_decrypt(key2->get_raw(), ciphertext);

CHECK_EQ(
ccf::ds::to_hex(expected_plaintext), ccf::ds::to_hex(decrypted_plaintext));
}

int main(int argc, char** argv)
{
ccf::crypto::openssl_sha256_init();
doctest::Context context;
context.applyCommandLine(argc, argv);
int res = context.run();
ccf::crypto::openssl_sha256_shutdown();
return res;
}
Loading