Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SPE-925] Add pcr provider manager for cage pcrs (#130)
- Loading branch information
1 parent
f390e37
commit 1d49c2b
Showing
8 changed files
with
276 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"evervault-python": minor | ||
--- | ||
|
||
Cage PCR Provider: publish new PCRs to public source which SDKs can pull from for attestation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import logging | ||
from evervault.threading.repeatedtimer import RepeatedTimer | ||
import threading | ||
import warnings | ||
import time | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class CagePcrManager: | ||
def __init__(self, attestation_data, poll_interval=300): | ||
self.attestation_data = attestation_data | ||
self.poll_interval = poll_interval | ||
self.lock = threading.Lock() | ||
self.store = {} | ||
|
||
self.__load_providers(attestation_data) | ||
self.__fetch_all_pcrs() | ||
|
||
logger.debug( | ||
"EVERVAULT :: Cage PCR manager starting polling for PCRs every {self.poll_interval} seconds" | ||
) | ||
|
||
self.repeated_timer = RepeatedTimer( | ||
self.poll_interval, | ||
self.__fetch_all_pcrs, | ||
) | ||
|
||
def get(self, cage_name) -> str: | ||
with self.lock: | ||
try: | ||
pcrs = self.store[cage_name]["pcrs"] | ||
if pcrs is None: | ||
raise KeyError | ||
return pcrs | ||
except KeyError: | ||
pcrs = self.__fetch_pcrs(cage_name) | ||
self.store[cage_name]["pcrs"] = pcrs | ||
return pcrs | ||
|
||
def get_poll_interval(self) -> list: | ||
return self.repeated_timer.get_interval() | ||
|
||
def disable_polling(self): | ||
if self.repeated_timer is not None: | ||
self.repeated_timer.stop() | ||
self.repeated_timer = None | ||
|
||
def clear_store(self): | ||
with self.lock: | ||
self.store = {} | ||
|
||
def remove_pcrs_for_cage(self, cage_name): | ||
with self.lock: | ||
self.store[cage_name]["pcrs"] = None | ||
|
||
def __create_provider_from_static_pcrs(self, pcrs): | ||
def provider(): | ||
return pcrs | ||
|
||
return provider | ||
|
||
def __load_providers(self, attestation_data): | ||
for cage_name, value in attestation_data.items(): | ||
if callable(value): | ||
self.store[cage_name] = {"pcrs": None, "provider": value} | ||
elif isinstance(value, list): | ||
self.store[cage_name] = { | ||
"pcrs": value, | ||
"provider": self.__create_provider_from_static_pcrs(value), | ||
} | ||
elif isinstance(value, dict): | ||
self.store[cage_name] = { | ||
"pcrs": [value], | ||
"provider": self.__create_provider_from_static_pcrs([value]), | ||
} | ||
|
||
else: | ||
raise Exception("EVERVAULT :: Invalid PCR data. Cannot create provider") | ||
|
||
def __fetch_all_pcrs(self): | ||
logger.debug("EVERVAULT :: Retrieving Cage PCRs from providers") | ||
for cage_name, provider in self.store.items(): | ||
pcrs = self.__fetch_pcrs(cage_name) | ||
self.store[cage_name]["pcrs"] = pcrs | ||
|
||
def __fetch_pcrs(self, cage_name): | ||
try: | ||
provider = self.store[cage_name]["provider"] | ||
|
||
if provider is None: | ||
warnings.warn( | ||
f"EVERVAULT :: No PCR provider registered for {cage_name}. Cannot fetch PCRs" | ||
) | ||
return None | ||
|
||
retries = 3 | ||
delay = 0.5 | ||
|
||
while retries > 0: | ||
try: | ||
pcrs = provider() | ||
logger.debug( | ||
"EVERVAULT :: Retrieved PCRs from provider for {cage_name}" | ||
) | ||
return pcrs | ||
except Exception as e: | ||
retries -= 1 | ||
if retries == 0: | ||
raise e | ||
else: | ||
time.sleep(delay) | ||
delay *= 2 | ||
warnings.warn( | ||
f"EVERVAULT :: Could not get PCR for {cage_name} {e}" | ||
) | ||
continue | ||
except KeyError: | ||
warnings.warn( | ||
f"EVERVAULT :: No PCR provider registered for {cage_name}. Cannot fetch PCRs" | ||
) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import unittest | ||
from evervault.http.cagePcrManager import CagePcrManager | ||
|
||
|
||
class TestCagePcrManager(unittest.TestCase): | ||
def setUp(self): | ||
self.app_uuid = "app-123" | ||
self.cage_1 = "cage_1" | ||
self.cage_2 = "cage_2" | ||
|
||
def test_get_pcrs(self): | ||
test_pcrs1 = [ | ||
{ | ||
"pcr_8": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
} | ||
] | ||
test_pcrs2 = [ | ||
{ | ||
"pcr_8": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" | ||
} | ||
] | ||
|
||
def test_provider1(): | ||
return test_pcrs1 | ||
|
||
def test_provider2(): | ||
return test_pcrs2 | ||
|
||
attestation_data = {self.cage_1: test_provider1, self.cage_2: test_provider2} | ||
|
||
manager = CagePcrManager(attestation_data) | ||
|
||
assert manager.get(self.cage_1) == test_pcrs1 | ||
assert manager.get(self.cage_2) == test_pcrs2 | ||
|
||
def test_get_hardcoded_pcrs(self): | ||
test_pcrs_object = { | ||
"pcr_8": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
} | ||
test_pcrs_array = [ | ||
{ | ||
"pcr_8": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
{ | ||
"pcr_8": "111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011" | ||
}, | ||
] | ||
attestation_data = {self.cage_1: test_pcrs_object, self.cage_2: test_pcrs_array} | ||
|
||
manager = CagePcrManager(attestation_data) | ||
|
||
assert manager.get(self.cage_1) == [test_pcrs_object] | ||
assert manager.get(self.cage_2) == test_pcrs_array | ||
|
||
def test_get_pcrs_reload_on_missing(self): | ||
test_pcrs1 = [ | ||
{ | ||
"pcr_8": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
} | ||
] | ||
|
||
def test_provider1(): | ||
return test_pcrs1 | ||
|
||
attestation_data = {self.cage_1: test_provider1} | ||
|
||
manager = CagePcrManager(attestation_data) | ||
|
||
# Remove pcrs for cage_1 so that we test the reload on a miss | ||
manager.remove_pcrs_for_cage(self.cage_1) | ||
|
||
assert manager.get(self.cage_1) == test_pcrs1 |