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

[common] Refactor Protected Files slightly #1841

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
744 changes: 343 additions & 401 deletions common/src/protected_files/protected_files.c

Large diffs are not rendered by default.

54 changes: 21 additions & 33 deletions common/src/protected_files/protected_files.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,24 @@
#include <stddef.h>
#include <stdint.h>

/*! Size of the AES-GCM encryption key */
#define PF_KEY_SIZE 16
#define PF_NODE_SIZE 4096U

/*! Size of IV for AES-GCM */
#define PF_IV_SIZE 12

/*! Size of MAC fields */
#define PF_MAC_SIZE 16
/*! Size of tag fields */
#define PF_TAG_SIZE 16

/*! Size of the AES-GCM encryption key */
#define PF_KEY_SIZE 16

/*! Size of the salt used in KDF (Key Derivation Function) */
#define PF_SALT_SIZE 32

typedef uint8_t pf_iv_t[PF_IV_SIZE];
typedef uint8_t pf_mac_t[PF_MAC_SIZE];
typedef uint8_t pf_tag_t[PF_TAG_SIZE];
typedef uint8_t pf_key_t[PF_KEY_SIZE];
typedef uint8_t pf_keyid_t[32]; /* key derivation material */

extern pf_key_t g_pf_mrenclave_key;
extern pf_key_t g_pf_mrsigner_key;
extern pf_key_t g_pf_wrap_key;
extern bool g_pf_wrap_key_set;
typedef uint8_t pf_salt_t[PF_SALT_SIZE];

typedef enum _pf_status_t {
PF_STATUS_SUCCESS = 0,
Expand All @@ -41,7 +41,7 @@ typedef enum _pf_status_t {
PF_STATUS_INVALID_VERSION = -6,
PF_STATUS_INVALID_HEADER = -7,
PF_STATUS_INVALID_PATH = -8,
PF_STATUS_MAC_MISMATCH = -9,
PF_STATUS_TAG_MISMATCH = -9,
PF_STATUS_NOT_IMPLEMENTED = -10,
PF_STATUS_CALLBACK_FAILED = -11,
PF_STATUS_PATH_TOO_LONG = -12,
Expand All @@ -55,8 +55,6 @@ typedef enum _pf_status_t {
#define PF_SUCCESS(status) ((status) == PF_STATUS_SUCCESS)
#define PF_FAILURE(status) ((status) != PF_STATUS_SUCCESS)

#define PF_NODE_SIZE 4096U

/*! PF open modes */
typedef enum _pf_file_mode_t {
PF_FILE_MODE_READ = 1,
Expand All @@ -66,6 +64,9 @@ typedef enum _pf_file_mode_t {
/*! Opaque file handle type, interpreted by callbacks as necessary */
typedef void* pf_handle_t;

/*! Context representing an open protected file */
typedef struct pf_context pf_context_t;

/*!
* \brief File read callback.
*
Expand Down Expand Up @@ -123,12 +124,12 @@ typedef void (*pf_debug_f)(const char* msg);
* \param key AES-GCM key.
* \param input Plaintext data.
* \param input_size Size of \p input in bytes.
* \param[out] mac MAC computed for \p input.
* \param[out] tag Authentication tag computed for \p input.
*
* \returns PF status.
*/
typedef pf_status_t (*pf_aes_cmac_f)(const pf_key_t* key, const void* input, size_t input_size,
pf_mac_t* mac);
pf_tag_t* tag);

/*!
* \brief AES-GCM encrypt callback.
Expand All @@ -140,13 +141,13 @@ typedef pf_status_t (*pf_aes_cmac_f)(const pf_key_t* key, const void* input, siz
* \param input Plaintext data.
* \param input_size Size of \p input in bytes.
* \param[out] output Buffer for encrypted data (size: \p input_size).
* \param[out] mac MAC computed for \p input and \p aad.
* \param[out] tag Authentication tag computed for \p input and \p aad.
*
* \returns PF status.
*/
typedef pf_status_t (*pf_aes_gcm_encrypt_f)(const pf_key_t* key, const pf_iv_t* iv, const void* aad,
size_t aad_size, const void* input, size_t input_size,
void* output, pf_mac_t* mac);
void* output, pf_tag_t* tag);

/*!
* \brief AES-GCM decrypt callback.
Expand All @@ -158,13 +159,13 @@ typedef pf_status_t (*pf_aes_gcm_encrypt_f)(const pf_key_t* key, const pf_iv_t*
* \param input Encrypted data.
* \param input_size Size of \p input in bytes.
* \param[out] output Buffer for decrypted data (size: \p input_size).
* \param mac Expected MAC.
* \param tag Expected authentication tag.
*
* \returns PF status.
*/
typedef pf_status_t (*pf_aes_gcm_decrypt_f)(const pf_key_t* key, const pf_iv_t* iv, const void* aad,
size_t aad_size, const void* input, size_t input_size,
void* output, const pf_mac_t* mac);
void* output, const pf_tag_t* tag);

/*!
* \brief Cryptographic random number generator callback.
Expand Down Expand Up @@ -197,9 +198,6 @@ void pf_set_callbacks(pf_read_f read_f, pf_write_f write_f, pf_fsync_f fsync_f,
pf_aes_gcm_decrypt_f aes_gcm_decrypt_f, pf_random_f random_f,
pf_debug_f debug_f);

/*! Context representing an open protected file */
typedef struct pf_context pf_context_t;

/* Public API */

/*!
Expand Down Expand Up @@ -296,16 +294,6 @@ pf_status_t pf_set_size(pf_context_t* pf, uint64_t size);
*/
pf_status_t pf_rename(pf_context_t* pf, const char* new_path);

/*!
* \brief Get underlying handle of a PF.
*
* \param pf PF context.
* \param[out] handle Handle to the backing file.
*
* \returns PF status.
*/
pf_status_t pf_get_handle(pf_context_t* pf, pf_handle_t* handle);

/*!
* \brief Flush any pending data of a protected file to disk.
*
Expand Down
130 changes: 58 additions & 72 deletions common/src/protected_files/protected_files_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#pragma once

#pragma pack(push, 1)

#ifdef USE_STDLIB
#include <assert.h>
#else
Expand All @@ -14,7 +16,6 @@

#include <limits.h>

#include "list.h"
#include "protected_files.h"

#define PF_FILE_ID 0x46505f5346415247 /* GRAFS_PF */
Expand All @@ -23,117 +24,102 @@

#define METADATA_KEY_NAME "SGX-PROTECTED-FS-METADATA-KEY"
#define MAX_LABEL_SIZE 64

static_assert(sizeof(METADATA_KEY_NAME) <= MAX_LABEL_SIZE, "label too long");

#pragma pack(push, 1)

typedef struct _metadata_plain {
uint64_t file_id;
uint8_t major_version;
uint8_t minor_version;
pf_keyid_t metadata_key_id;
pf_mac_t metadata_gmac; /* GCM mac */
} metadata_plain_t;

#define PATH_MAX_SIZE (260 + 512)

// these are all defined as relative to node size, so we can decrease node size in tests
// and have deeper tree
#define MD_USER_DATA_SIZE (PF_NODE_SIZE * 3 / 4) // 3072
#define MD_USER_DATA_SIZE (PF_NODE_SIZE * 3 / 4)
static_assert(MD_USER_DATA_SIZE == 3072, "bad struct size");

typedef struct _metadata_encrypted {
char path[PATH_MAX_SIZE];
uint64_t size;
pf_key_t mht_key;
pf_mac_t mht_gmac;
uint8_t data[MD_USER_DATA_SIZE];
} metadata_encrypted_t;

typedef uint8_t metadata_encrypted_blob_t[sizeof(metadata_encrypted_t)];

#define METADATA_NODE_SIZE PF_NODE_SIZE

typedef uint8_t metadata_padding_t[METADATA_NODE_SIZE -
(sizeof(metadata_plain_t) + sizeof(metadata_encrypted_blob_t))];

typedef struct _metadata_node {
metadata_plain_t plain_part;
metadata_encrypted_blob_t encrypted_part;
metadata_padding_t padding;
} metadata_node_t;
#define MAX_PAGES_IN_CACHE 48

static_assert(sizeof(metadata_node_t) == PF_NODE_SIZE, "sizeof(metadata_node_t)");
enum {
FILE_MHT_NODE_TYPE = 1,
FILE_DATA_NODE_TYPE = 2,
};

typedef struct _data_node_crypto {
typedef struct _gcm_crypto_data {
pf_key_t key;
pf_mac_t gmac;
pf_tag_t tag;
} gcm_crypto_data_t;

// for PF_NODE_SIZE == 4096, we have 96 attached data nodes and 32 mht child nodes
// for PF_NODE_SIZE == 2048, we have 48 attached data nodes and 16 mht child nodes
// for PF_NODE_SIZE == 1024, we have 24 attached data nodes and 8 mht child nodes
// 3/4 of the node size is dedicated to data nodes
// for PF_NODE_SIZE == 4096, we have 96 attached data nodes and 32 MHT child nodes
// 3/4 of the node is dedicated to data nodes, 1/4 to MHT nodes
#define ATTACHED_DATA_NODES_COUNT ((PF_NODE_SIZE / sizeof(gcm_crypto_data_t)) * 3 / 4)
#define CHILD_MHT_NODES_COUNT ((PF_NODE_SIZE / sizeof(gcm_crypto_data_t)) * 1 / 4)
static_assert(ATTACHED_DATA_NODES_COUNT == 96, "ATTACHED_DATA_NODES_COUNT");
// 1/4 of the node size is dedicated to child mht nodes
#define CHILD_MHT_NODES_COUNT ((PF_NODE_SIZE / sizeof(gcm_crypto_data_t)) * 1 / 4)
static_assert(CHILD_MHT_NODES_COUNT == 32, "CHILD_MHT_NODES_COUNT");

typedef struct _metadata_plaintext_header {
uint64_t file_id;
uint8_t major_version;
uint8_t minor_version;
pf_salt_t metadata_key_salt;
pf_tag_t metadata_tag; /* GCM MAC aka Authentication Tag */
} metadata_plaintext_header_t;

typedef struct _metadata_decrypted_header {
char file_path[PATH_MAX_SIZE];
uint64_t file_size;
pf_key_t root_mht_node_key;
pf_tag_t root_mht_node_tag;
uint8_t file_data[MD_USER_DATA_SIZE];
} metadata_decrypted_header_t;

typedef uint8_t metadata_encrypted_blob_t[sizeof(metadata_decrypted_header_t)];

typedef uint8_t metadata_padding_t[PF_NODE_SIZE -
(sizeof(metadata_plaintext_header_t) +
sizeof(metadata_encrypted_blob_t))];

typedef struct _metadata_node {
metadata_plaintext_header_t plaintext_header;
metadata_encrypted_blob_t encrypted_blob;
metadata_padding_t padding;
} metadata_node_t;
static_assert(sizeof(metadata_node_t) == PF_NODE_SIZE, "sizeof(metadata_node_t)");

typedef struct _mht_node {
gcm_crypto_data_t data_nodes_crypto[ATTACHED_DATA_NODES_COUNT];
gcm_crypto_data_t mht_nodes_crypto[CHILD_MHT_NODES_COUNT];
} mht_node_t;

static_assert(sizeof(mht_node_t) == PF_NODE_SIZE, "sizeof(mht_node_t)");

typedef struct _data_node {
uint8_t data[PF_NODE_SIZE];
uint8_t bytes[PF_NODE_SIZE];
} data_node_t;

static_assert(sizeof(data_node_t) == PF_NODE_SIZE, "sizeof(data_node_t)");

typedef struct _encrypted_node {
uint8_t cipher[PF_NODE_SIZE];
uint8_t bytes[PF_NODE_SIZE];
} encrypted_node_t;

static_assert(sizeof(encrypted_node_t) == PF_NODE_SIZE, "sizeof(encrypted_node_t)");

#define MAX_PAGES_IN_CACHE 48

typedef enum {
FILE_MHT_NODE_TYPE = 1,
FILE_DATA_NODE_TYPE = 2,
} mht_node_type_e;

// make sure these are the same size
static_assert(sizeof(mht_node_t) == sizeof(data_node_t),
"sizeof(mht_node_t) == sizeof(data_node_t)");
static_assert(sizeof(mht_node_t) == sizeof(data_node_t), "sizes of MHT and data nodes differ");

DEFINE_LIST(_file_node);
// Data struct that wraps the 4KB encrypted-node buffer (bounce buffer) and the corresponding 4KB
// decrypted-data buffer (plain buffer), plus additional fields. This data struct is used for both
// Data and MHT nodes (but not for Metadata node).
typedef struct _file_node {
LIST_TYPE(_file_node) list;
uint8_t type;
uint64_t node_number;
struct _file_node* parent;
bool need_writing;
struct {
uint64_t physical_node_number;
encrypted_node_t encrypted; // the actual data from the disk
};
union { // decrypted data
struct _file_node* parent;

uint64_t logical_node_number;
uint64_t physical_node_number;

encrypted_node_t encrypted; // encrypted data from storage (bounce buffer)
union { // decrypted data in private memory (plain buffer)
mht_node_t mht;
data_node_t data;
} decrypted;
} file_node_t;
DEFINE_LISTP(_file_node);

typedef struct {
uint32_t index;
uint32_t index; // always "1"; FIXME: what's the point of this?
char label[MAX_LABEL_SIZE]; // must be NULL terminated
pf_keyid_t nonce;
uint32_t output_len; // in bits
pf_salt_t key_salt; // salt for key derivation from KDK, stored in metadata node
uint32_t output_len; // in bits; always 128; FIXME: what's the point of this?
} kdf_input_t;

#pragma pack(pop)
27 changes: 16 additions & 11 deletions common/src/protected_files/protected_files_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@
#include "protected_files_format.h"

struct pf_context {
metadata_node_t file_metadata; // actual data from disk's meta data node
pf_status_t last_error;
metadata_encrypted_t encrypted_part_plain; // encrypted part of metadata node, decrypted
file_node_t root_mht; // the root of the mht is always needed (for files bigger than 3KB)
pf_handle_t file;
pf_file_mode_t mode;
bool need_writing;
pf_status_t file_status;
pf_key_t user_kdk_key;
lruc_context_t* cache;
pf_handle_t host_file_handle; // opaque file handle (e.g. PAL handle) used by callbacks
pf_file_mode_t mode; // read-only, write-only or read-write
bool need_writing; // whether file was modified and thus needs writing to storage

pf_status_t file_status; // PF_STATUS_SUCCESS, PF_STATUS_CRYPTO_ERROR, etc.
pf_status_t last_error; // FIXME: unclear why this is needed

pf_key_t kdk; // KDK installed by user of PF (e.g. from Gramine manifest)

metadata_node_t metadata_node; // plaintext and encrypted metadata from storage (bounce buffer)
metadata_decrypted_header_t metadata_decrypted_header; // contains file path, size, etc.

file_node_t root_mht_node; // root MHT node is always needed (for files bigger than 3KB)

lruc_context_t* cache; // up to MAX_PAGES_IN_CACHE nodes are cached for each file
#ifdef DEBUG
char* debug_buffer; // buffer for debug output
char* debug_buffer; // buffer for debug output
#endif
};
Loading