33import base64
44import binascii
55import pathlib
6+ import shelve
67
78from cryptography .hazmat .primitives import serialization
89from 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