Skip to content
This repository was archived by the owner on Jan 27, 2022. It is now read-only.

Commit f6c27ba

Browse files
author
manju956
committed
Implementation of Enclave Key Refresh
This feature initiates refresh of enclave encryption key pair based on timer value set in the config file. A new pair of encryption key is generated in the enclave and the updated enclave signup details are stored in the KvStorage in workers table. Additioanlly, encryption key signature is also generated during enclave initialization and its made part of PublicEnclaveData. This encryption key signature is re-computed when encryption key gets refreshed. Signed-off-by: manju956 <[email protected]>
1 parent 4b45381 commit f6c27ba

File tree

17 files changed

+482
-49
lines changed

17 files changed

+482
-49
lines changed

common/cpp/error.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ namespace tcf {
4646
) : Error(TCF_ERR_CRYPTO, msg) {}
4747
}; // class CryptoError
4848

49+
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
50+
class KeyRefreshError : public Error {
51+
public:
52+
explicit KeyRefreshError(
53+
const std::string& msg
54+
) : Error(TCF_ERR_ENCRYPT_KEY_REFRESH, msg) {}
55+
}; // class KeyRefreshError
4956

5057
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5158
class MemoryError : public Error {

common/cpp/json_utils.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,3 @@ void JsonSetNumber(JSON_Object* json, const char* name, double value, const char
5555
JSON_Status jret = json_object_dotset_number(json, name, value);
5656
tcf::error::ThrowIf<tcf::error::RuntimeError>(jret != JSONSuccess, err);
5757
}
58-

common/cpp/tcf_error.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ typedef enum {
3535
a TCF_ERR_SYSTEM for reporting.
3636
*/
3737
TCF_ERR_CRYPTO = -11,
38-
TCF_ERR_INVALID_WORKLOAD = -12 /* Invalid workload id */
38+
TCF_ERR_INVALID_WORKLOAD = -12, /* Invalid workload id */
39+
TCF_ERR_ENCRYPT_KEY_REFRESH = -13 /* Enclave key refresh error */
3940
} tcf_err_t;
4041

4142
typedef enum {

common/cpp/utils.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,3 @@
2020
ByteArray StrToByteArray(std::string str);
2121

2222
std::string ByteArrayToStr(ByteArray ba);
23-

config/tcs_config.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,7 @@ DataEncryptionAlgorithm = "AES-GCM-256"
9999
# starting with tilde "~"
100100
workOrderPayloadFormats = "JSON-RPC"
101101

102+
[WorkerKeyRefresh]
103+
# Configure key refresh interval based on need.
104+
# By default, key refresh feature is disabled.
105+
key_refresh_interval = 0

enclave_manager/avalon_enclave_manager/avalon_enclave_bridge.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ def create_signup_info(originator_public_key_hash, nonce):
210210
signup_info = {
211211
'verifying_key': signup_data['verifying_key'],
212212
'encryption_key': signup_data['encryption_key'],
213+
'encryption_key_signature': signup_data['encryption_key_signature'],
213214
'proof_data': 'Not present',
214215
'enclave_persistent_id': 'Not present'
215216
}
@@ -263,3 +264,24 @@ def create_signup_info(originator_public_key_hash, nonce):
263264

264265
# Now we can return the real object
265266
return signup_info_obj
267+
268+
269+
def refresh_worker_encryption_key(enclave_sealed_data):
270+
"""
271+
Refresh worker encryption key pair
272+
"""
273+
# start enclave key refresh
274+
updated_signup_data = enclave.RefreshWorkerEncryptionKey(
275+
enclave_sealed_data)
276+
if updated_signup_data is None:
277+
return None
278+
signup_info = {
279+
'encryption_key':
280+
updated_signup_data['encryption_key'],
281+
'encryption_key_signature':
282+
updated_signup_data['encryption_key_signature'],
283+
'sealed_enclave_data':
284+
updated_signup_data['sealed_enclave_data']
285+
}
286+
287+
return signup_info

enclave_manager/avalon_enclave_manager/avalon_enclave_helper.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def create_enclave_signup_data(cls, tcf_instance_keys=None):
6868
enclave_info['sealed_data'] = enclave_data.sealed_signup_data
6969
enclave_info['verifying_key'] = enclave_data.verifying_key
7070
enclave_info['encryption_key'] = enclave_data.encryption_key
71+
enclave_info['encryption_key_signature'] = \
72+
enclave_data.encryption_key_signature
7173
enclave_info['enclave_id'] = enclave_data.verifying_key
7274
enclave_info['proof_data'] = ''
7375
if not avalon_enclave.enclave.is_sgx_simulator():
@@ -87,6 +89,8 @@ def __init__(self, enclave_info, tcf_instance_keys):
8789
self.sealed_data = enclave_info['sealed_data']
8890
self.verifying_key = enclave_info['verifying_key']
8991
self.encryption_key = enclave_info['encryption_key']
92+
self.encryption_key_signature = \
93+
enclave_info['encryption_key_signature']
9094
self.proof_data = enclave_info['proof_data']
9195
self.enclave_id = enclave_info['enclave_id']
9296
except KeyError as ke:
@@ -96,6 +100,23 @@ def __init__(self, enclave_info, tcf_instance_keys):
96100
self.enclave_keys = \
97101
keys.EnclaveKeys(self.verifying_key, self.encryption_key)
98102

103+
# -------------------------------------------------------
104+
def initiate_refresh_worker_encryption_key(self):
105+
"""
106+
Initiate worker encryption key refresh and update worker signup details
107+
"""
108+
try:
109+
updated_enclave_data = \
110+
avalon_enclave.refresh_worker_encryption_key(self.sealed_data)
111+
except Exception as err:
112+
raise Exception('failed to refresh enclave key; {}'
113+
.format(str(err)))
114+
self.encryption_key = updated_enclave_data["encryption_key"]
115+
self.encryption_key_signature = \
116+
updated_enclave_data["encryption_key_signature"]
117+
self.sealed_data = updated_enclave_data["sealed_enclave_data"]
118+
return self
119+
99120
# -------------------------------------------------------
100121
def send_to_sgx_worker(self, encrypted_request):
101122
"""

enclave_manager/avalon_enclave_manager/enclave_manager.py

Lines changed: 112 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
import os
2121
import sys
2222
import time
23-
import random
2423
import hashlib
24+
import secrets
25+
import asyncio
2526

2627
import avalon_enclave_manager.sgx_work_order_request as work_order_request
2728
import avalon_enclave_manager.avalon_enclave_helper as enclave_helper
@@ -54,12 +55,9 @@ def __init__(self, config, signup_data, measurements):
5455
self.verifying_key = signup_data.verifying_key
5556
self.encryption_key = signup_data.encryption_key
5657

57-
# TODO: EncryptionKeyNonce and EncryptionKeySignature are hardcoded
58-
# to dummy values.
59-
# Need to come up with a scheme to generate both for every unique
60-
# encryption key.
61-
self.encryption_key_nonce = ""
62-
self.encryption_key_signature = ""
58+
self.encryption_key_signature = signup_data.encryption_key_signature
59+
# Generate random 32 byte nonce in hex format
60+
self.encryption_key_nonce = secrets.token_hex(32)
6361
self.enclave_id = signup_data.enclave_id
6462
self.extended_measurements = measurements
6563

@@ -92,11 +90,8 @@ def manager_on_boot(self, kv_helper):
9290

9391
worker_info = create_json_worker(self, self.config)
9492
logger.info("Adding enclave workers to workers table")
95-
worker_id = crypto_utils.strip_begin_end_public_key(self.enclave_id) \
96-
.encode("UTF-8")
97-
# Calculate sha256 of worker id to get 32 bytes. The TC spec proxy
98-
# model contracts expect byte32. Then take a hexdigest for hex str.
99-
worker_id = hashlib.sha256(worker_id).hexdigest()
93+
worker_id = crypto_utils.calculate_worker_id_from_public_key(
94+
self.enclave_id)
10095

10196
kv_helper.set("workers", worker_id, worker_info)
10297

@@ -159,7 +154,6 @@ def manager_on_boot(self, kv_helper):
159154

160155
# End of for-loop
161156

162-
# -----------------------------------------------------------------
163157
def process_work_orders(self, kv_helper):
164158
"""
165159
Executes Run time flow of enclave manager
@@ -255,7 +249,6 @@ def process_work_orders(self, kv_helper):
255249
# end of for loop
256250

257251
# -----------------------------------------------------------------
258-
259252
def __update_receipt(self, kv_helper, wo_id, wo_json_resp):
260253
"""
261254
Update the existing work order receipt with the status as in wo_json_
@@ -330,6 +323,71 @@ def create_enclave_signup_data():
330323
return enclave_signup_data
331324

332325

326+
# -----------------------------------------------------------------
327+
async def wo_process(config, kv_helper, enclave_manager):
328+
"""
329+
Process work order requests in TEE and persist result in KvStorage
330+
331+
@param enclave_manager - instance of the EnclaveManager class
332+
@param kv_helper - instance of KvStorage
333+
@param config - instance of the configuration parsed while bringing up
334+
enclave manager service
335+
"""
336+
try:
337+
sleep_interval = int(config["EnclaveManager"]["sleep_interval"])
338+
except Exception as err:
339+
logger.error("Failed to get sleep interval from config file. " +
340+
"Setting sleep interval to 10 seconds: %s", str(err))
341+
sleep_interval = 10
342+
343+
while True:
344+
try:
345+
enclave_manager.process_work_orders(kv_helper)
346+
except Exception as e:
347+
logger.error("Error while processing work-order")
348+
logger.error("Exception: {} args {} details {}".format(type(e),
349+
e.args, e))
350+
await asyncio.sleep(sleep_interval)
351+
352+
353+
# -----------------------------------------------------------------
354+
async def initiate_key_refresh(config, kv_helper, enclave_manager):
355+
356+
"""
357+
Initiate worker encryption key refresh and update worker details
358+
and updates worker details to kv storage
359+
@param lock - ayncio lock instance
360+
@param config - instance of configuration parsed to enclave manager process
361+
@param kv_helper - instance of KvStorage
362+
@param enclave_manager - instance of enclave manager
363+
"""
364+
try:
365+
key_refresh_interval = \
366+
int(config["WorkerKeyRefresh"]["key_refresh_interval"])
367+
if key_refresh_interval <= 0:
368+
return
369+
except Exception as err:
370+
logger.error("Failed to get key refresh interval from config file",
371+
str(err))
372+
return
373+
asyncio.sleep(key_refresh_interval)
374+
while True:
375+
try:
376+
enclave_data = enclave_manager.enclave_data
377+
enclave_signup_data = \
378+
enclave_data.initiate_refresh_worker_encryption_key()
379+
# Update Enclave Manager with updated signup data after key refresh
380+
enclave_manager = EnclaveManager(
381+
config, enclave_signup_data,
382+
enclave_manager.extended_measurements)
383+
# Persist updated worker to KV storage
384+
persist_worker(enclave_manager, kv_helper)
385+
except Exception as e:
386+
logger.error("failed to get signup data after key refresh: %s",
387+
str(e))
388+
await asyncio.sleep(key_refresh_interval)
389+
390+
333391
# -----------------------------------------------------------------
334392
def execute_work_order(enclave_data, input_json_str, indent=4):
335393
"""
@@ -385,6 +443,8 @@ def create_json_worker(enclave_data, config):
385443
worker_type_data["encryptionKey"] = enclave_data.encryption_key
386444
worker_type_data["encryptionKeySignature"] = \
387445
enclave_data.encryption_key_signature
446+
worker_type_data["encryptionKeyNonce"] = \
447+
enclave_data.encryption_key_nonce
388448

389449
worker_info = dict()
390450
worker_info["workerType"] = WorkerType.TEE_SGX.value
@@ -430,6 +490,37 @@ def create_json_worker(enclave_data, config):
430490
return json_worker_info
431491

432492

493+
# -----------------------------------------------------------------
494+
def persist_worker(enclave_manager, kv_helper):
495+
"""
496+
Persists worker to KvStorage
497+
498+
@param enclave_manager - instance of EnclaveManager class
499+
@param kv_helper - instance of KvStorage
500+
"""
501+
worker_info = create_json_worker(enclave_manager, enclave_manager.config)
502+
worker_id = crypto_utils.calculate_worker_id_from_public_key(
503+
enclave_manager.enclave_id)
504+
logger.info("Persisting worker to workers table")
505+
kv_helper.set("workers", worker_id, worker_info)
506+
507+
508+
async def execute_tasks(enclave_manager, kv_helper, config):
509+
"""
510+
Executes worker encryption key refresh task and work order execution
511+
inside TEE asynchronously.
512+
513+
@param enclave_manager - instance of the EnclaveManager class
514+
@param kv_helper - instance of KvStorage
515+
@param config - instance of the configuration parsed while bringing up
516+
enclave manager service
517+
"""
518+
await asyncio.wait([
519+
wo_process(config, kv_helper, enclave_manager),
520+
initiate_key_refresh(config, kv_helper, enclave_manager)
521+
])
522+
523+
433524
# -----------------------------------------------------------------
434525
def start_enclave_manager(config):
435526
"""
@@ -475,23 +566,19 @@ def start_enclave_manager(config):
475566

476567
try:
477568
sleep_interval = int(config["EnclaveManager"]["sleep_interval"])
569+
key_refresh_interval = \
570+
int(config["WorkerKeyRefresh"]["key_refresh_interval"])
478571
except Exception as err:
479572
logger.error("Failed to get sleep interval from config file. " +
480573
"Setting sleep interval to 10 seconds: %s", str(err))
481574
sleep_interval = 10
482575

576+
event_loop = asyncio.get_event_loop()
483577
try:
484-
while True:
485-
# Poll KV storage for new work-order requests and process
486-
enclave_manager.process_work_orders(kv_helper)
487-
logger.info("Enclave manager sleeping for %d secs", sleep_interval)
488-
time.sleep(sleep_interval)
489-
except Exception as inst:
490-
logger.error("Error while processing work-order; " +
491-
"shutting down enclave manager")
492-
logger.error("Exception: {} args {} details {}".format(type(inst),
493-
inst.args, inst))
494-
exit(1)
578+
event_loop.run_until_complete(
579+
execute_tasks(enclave_manager, kv_helper, config))
580+
finally:
581+
event_loop.close()
495582

496583

497584
TCFHOME = os.environ.get("TCF_HOME", "../../../../")

tc/sgx/trusted_worker_manager/enclave/enclave_data.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
#include "jsonvalue.h"
3333
#include "parson.h"
34+
#include "base64.h"
35+
#include "enclave_utils.h"
3436

3537
#include "enclave_data.h"
3638

@@ -57,6 +59,8 @@ EnclaveData::EnclaveData(void) {
5759
// Create the public encryption key
5860
public_encryption_key_ = private_encryption_key_.GetPublicKey();
5961

62+
// Create encryption key signature
63+
generate_encryption_key_signature();
6064
SerializePrivateData();
6165
SerializePublicData();
6266
}
@@ -74,15 +78,11 @@ EnclaveData::~EnclaveData(void) {
7478
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7579
EnclaveData::EnclaveData(const uint8_t* inSealedData) {
7680
tcf::error::ThrowIfNull(inSealedData, "Sealed sign up data pointer is NULL");
77-
7881
uint32_t decrypted_size =
7982
sgx_get_encrypt_txt_len(reinterpret_cast<const sgx_sealed_data_t*>(inSealedData));
8083

81-
// Need to check for error
82-
8384
std::vector<uint8_t> decrypted_data;
8485
decrypted_data.resize(decrypted_size);
85-
8686
// Unseal the data
8787
sgx_status_t ret = sgx_unseal_data(reinterpret_cast<const sgx_sealed_data_t*>(inSealedData),
8888
nullptr, 0, &decrypted_data[0], &decrypted_size);
@@ -95,6 +95,9 @@ EnclaveData::EnclaveData(const uint8_t* inSealedData) {
9595
decrypted_data.clear();
9696
decrypted_data_string.clear();
9797

98+
/* Create encryption key signature */
99+
generate_encryption_key_signature();
100+
98101
SerializePrivateData();
99102
SerializePublicData();
100103
}
@@ -263,6 +266,14 @@ void EnclaveData::SerializePublicData(void) {
263266
tcf::error::ThrowIf<tcf::error::RuntimeError>(
264267
jret != JSONSuccess, "enclave data serialization failed on public encryption key");
265268

269+
// Public encryption key signature
270+
jret =
271+
json_object_dotset_string(dataObject, "EncryptionKeySignature", \
272+
encryption_key_signature_.c_str());
273+
tcf::error::ThrowIf<tcf::error::RuntimeError>(
274+
jret != JSONSuccess, \
275+
"enclave data serialization failed on public encryption key signature");
276+
266277
size_t serializedSize = json_serialization_size(dataValue);
267278

268279
std::vector<char> serialized_buffer;

0 commit comments

Comments
 (0)