Skip to content

Commit 38def40

Browse files
committed
database: optional persistent cache for keys
Add the (very insecure) option to persist the shared keys to disk for later offline use. This should ONLY be used in contexts where security is not a consideration and offline operation is required. Signed-off-by: Jordan Yates <jordan@embeint.com>
1 parent 3c32d77 commit 38def40

1 file changed

Lines changed: 35 additions & 5 deletions

File tree

src/infuse_iot/database.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import base64
44
import binascii
55
import pathlib
6+
import shelve
67

78
from cryptography.hazmat.primitives import serialization
89
from cryptography.hazmat.primitives.asymmetric import x25519
@@ -65,12 +66,17 @@ def gatt_sequence_num(self):
6566
self._tx_gatt_seq += 1
6667
return self._tx_gatt_seq
6768

68-
def __init__(self, local_root: pathlib.Path | None) -> None:
69+
def __init__(
70+
self,
71+
local_root: pathlib.Path | None,
72+
cache_path: pathlib.Path | None = None,
73+
) -> None:
6974
self.gateway: int | None = None
7075
self.devices: dict[int, DeviceDatabase.DeviceState] = {}
7176
self.bt_addr: dict[InterfaceAddress.BluetoothLeAddr, int] = {}
7277
self._local_root: x25519.X25519PrivateKey | None = None
7378
self._local_root_public: bytes | None = None
79+
self._cache_path = cache_path
7480
if local_root:
7581
with local_root.open() as f:
7682
private_key = serialization.load_pem_private_key(f.read().encode("utf-8"), password=None)
@@ -124,6 +130,23 @@ def observe_secondary_remote_public_key(self, infuse_id: int, secondary_pub_key:
124130
dev.secondary_device_key_id = binascii.crc32(self._local_root_public + dev.device_public_key) & 0xFFFFFF
125131
dev.local_shared_key = self._local_root.exchange(device_public_key)
126132

133+
def _update_cache(self, infuse_id: int, device_pub_key: bytes, shared_key: bytes):
134+
if self._cache_path is None:
135+
return
136+
infuse_id_str = f"{infuse_id:016x}"
137+
with shelve.open(self._cache_path) as cache:
138+
cache[infuse_id_str] = {"public_key": device_pub_key, "shared_key": shared_key}
139+
140+
def _from_cache(self, infuse_id: int, device_pub_key: bytes) -> bytes | None:
141+
if self._cache_path is None:
142+
return None
143+
infuse_id_str = f"{infuse_id:016x}"
144+
with shelve.open(self._cache_path) as cache:
145+
state = cache.get(infuse_id_str, None)
146+
if state is None or state["public_key"] != device_pub_key:
147+
return None
148+
return state["shared_key"]
149+
127150
def observe_security_state(
128151
self, infuse_id: int, cloud_pub_key: bytes, device_pub_key: bytes, network_id: int
129152
) -> None:
@@ -139,10 +162,17 @@ def observe_security_state(
139162

140163
with client as client:
141164
body = Key(base64.b64encode(device_pub_key).decode("utf-8"))
142-
response = get_shared_secret.sync(client=client, body=body)
143-
if response is not None:
144-
key = base64.b64decode(response.key)
145-
self.devices[infuse_id].shared_key = key
165+
try:
166+
response = get_shared_secret.sync(client=client, body=body)
167+
if response is not None:
168+
key = base64.b64decode(response.key)
169+
self.devices[infuse_id].shared_key = key
170+
self._update_cache(infuse_id, device_pub_key, key)
171+
except Exception as e:
172+
cache_key = self._from_cache(infuse_id, device_pub_key)
173+
if cache_key is None:
174+
raise e
175+
self.devices[infuse_id].shared_key = cache_key
146176

147177
def _network_key(self, network_id: int, interface: bytes, gps_time: int) -> bytes:
148178
if network_id not in self._network_keys:

0 commit comments

Comments
 (0)