comms.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #ifndef __COMMS_HPP__
  2. #define __COMMS_HPP__
  3. #include <vector>
  4. #include "enclave_api.h"
  5. // What step of the handshake are we on?
  6. enum HandshakeStep {
  7. HANDSHAKE_NONE,
  8. HANDSHAKE_C_SENT_1,
  9. HANDSHAKE_S_SENT_2,
  10. HANDSHAKE_COMPLETE
  11. };
  12. // Communication state for a node
  13. struct NodeCommState {
  14. sgx_ec256_public_t pubkey;
  15. nodenum_t node_num;
  16. HandshakeStep handshake_step;
  17. // Our DH keypair during the handshake
  18. sgx_ec256_private_t handshake_dh_privkey;
  19. sgx_ec256_public_t handshake_dh_pubkey;
  20. // The server keeps this state between handshake messages 1 and 3
  21. uint8_t handshake_cli_srv_mac[16];
  22. // The outgoing and incoming AES keys after the handshake
  23. sgx_aes_gcm_128bit_key_t out_aes_key, in_aes_key;
  24. // The outgoing and incoming IV counters
  25. uint8_t out_aes_iv[SGX_AESGCM_IV_SIZE];
  26. uint8_t in_aes_iv[SGX_AESGCM_IV_SIZE];
  27. // The GCM state for incrementally building each outgoing chunk
  28. sgx_aes_state_handle_t out_aes_gcm_state;
  29. // The current outgoing frame and the current offset into it
  30. uint8_t *frame;
  31. uint32_t frame_offset;
  32. // The current outgoing message ciphertext size and the offset into
  33. // it of the start of the current frame
  34. uint32_t msg_size;
  35. uint32_t msg_frame_offset;
  36. // The current outgoing message plaintext size, how many plaintext
  37. // bytes we've already processed with message_data, and how many
  38. // plaintext bytes remain for the current chunk
  39. uint32_t msg_plaintext_size;
  40. uint32_t msg_plaintext_processed;
  41. uint32_t msg_plaintext_chunk_remain;
  42. // The current incoming message ciphertext size and the offset into
  43. // it of all previous chunks of this message
  44. uint32_t in_msg_size;
  45. uint32_t in_msg_offset;
  46. // The current incoming message number of plaintext bytes processed
  47. uint32_t in_msg_plaintext_processed;
  48. // The internal buffer where we're storing the (decrypted) message
  49. uint8_t *in_msg_buf;
  50. // The function to call when a new incoming message header arrives.
  51. // This function should return a pointer to enough memory to hold
  52. // the (decrypted) chunks of the message. Remember that the length
  53. // passed here is the total size of the _encrypted_ chunks. This
  54. // function should not itself modify the in_msg_size, in_msg_offset,
  55. // or in_msg_buf members. This function will usually allocate an
  56. // appropriate amount of memory and return the pointer to it, but
  57. // may do other things, like return a pointer to the middle of a
  58. // previously allocated region of memory.
  59. std::function<uint8_t*(NodeCommState&,uint32_t)> in_msg_get_buf;
  60. // The function to call after the last chunk of a message has been
  61. // received. If in_msg_get_buf allocated memory, this function
  62. // should deallocate it. in_msg_size, in_msg_offset,
  63. // in_msg_plaintext_processed, and in_msg_buf will already have been
  64. // reset when this function is called. The uint32_t that is passed
  65. // are the total size of the _decrypted_ data and the original total
  66. // size of the _encrypted_ chunks that was passed to in_msg_get_buf.
  67. std::function<void(NodeCommState&,uint8_t*,uint32_t,uint32_t)>
  68. in_msg_received;
  69. NodeCommState(const sgx_ec256_public_t* conf_pubkey, nodenum_t i) :
  70. node_num(i), handshake_step(HANDSHAKE_NONE),
  71. out_aes_gcm_state(NULL), frame(NULL),
  72. frame_offset(0), msg_size(0), msg_frame_offset(0),
  73. msg_plaintext_size(0), msg_plaintext_processed(0),
  74. msg_plaintext_chunk_remain(0),
  75. in_msg_size(0), in_msg_offset(0),
  76. in_msg_plaintext_processed(0), in_msg_buf(NULL),
  77. in_msg_get_buf(NULL), in_msg_received(NULL) {
  78. memmove(&pubkey, conf_pubkey, sizeof(pubkey));
  79. }
  80. void message_start(uint32_t plaintext_len, bool encrypt=true);
  81. void message_data(const uint8_t *data, uint32_t len, bool encrypt=true);
  82. // Start the handshake (as the client)
  83. void handshake_start();
  84. };
  85. // The communication states for all the nodes. There's an entry for
  86. // ourselves in here, but it is unused.
  87. extern std::vector<NodeCommState> g_commstates;
  88. // A typical default in_msg_get_buf handler. It computes the maximum
  89. // possible size of the decrypted data, allocates that much memory, and
  90. // returns a pointer to it.
  91. uint8_t* default_in_msg_get_buf(NodeCommState &commst,
  92. uint32_t tot_enc_chunk_size);
  93. // An in_msg_received handler when we don't actually expect a message
  94. // from a given node at a given time.
  95. void unknown_in_msg_received(NodeCommState &nodest,
  96. uint8_t *data, uint32_t plaintext_len, uint32_t);
  97. // The enclave-to-enclave communication protocol is as follows. It
  98. // probably could just be attested TLS in a production environment, but
  99. // we're not implementing remote attestation at this time. This means
  100. // that the list of other enclaves' public keys are currently just
  101. // blindly trusted, so add a remote attestation step to validate them if
  102. // you want to deploy this for real.
  103. //
  104. // The protocol starts with a Sign-and-MAC (SIGMA) handshake, in the
  105. // pre-specified peer setting. The client is the lower-numbered node,
  106. // and the server is the higher-numbered node. The protocol is:
  107. //
  108. // Message 1 C -> S: g^x
  109. // Message 2 S -> C: g^y, Sig_S(MAC_{H_1a(g^{xy})}(g^y, g^x, Pub_S, Pub_C)
  110. // Message 3 C -> S: Sig_C(MAC_{H_1b(g^{xy})}(g^x, g^y, Pub_C, Pub_S)
  111. //
  112. // where Pub_C and Pub_S are the long-term signature keys of C and S.
  113. //
  114. // After the handshake, the client-to-server AES-GCM key is set to
  115. // H_2a(g^{xy}) and the server-to-client AES-GCM key is set to
  116. // H_2b(g^{xy}). H_na(x) and H_nb(x) are the first 128 bits and the
  117. // last 128 bits of SHA256(n || x) respectively.
  118. //
  119. // After the handshake, data is sent in logical messages, which are
  120. // divided into chunks of size at most FRAME_SIZE - SGX_AESGCM_MAC_SIZE
  121. // bytes of plaintext, which will expand to at most FRAME_SIZE bytes of
  122. // ciphertext. The IV for the first chunk in each direction is
  123. // 0x01 0x00 0x00 ... 0x00 (remember they use different keys in the two
  124. // directions), and each chunk increments the IV in a little-endian
  125. // manner. The MAC tag of SGX_AESGCM_MAC_SIZE bytes is at the end of
  126. // the chunk.
  127. bool comms_init_nodestate(const EnclaveAPINodeConfig *apinodeconfigs,
  128. nodenum_t num_nodes, nodenum_t my_node_num);
  129. #endif