def test_crypto_hash_sha256(self): self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("test")), "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("howdy")), "0f1128046248f83dc9b9ab187e16fad0ff596128f1524d05a9a77c4ad932f10a") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("Correct Horse Battery Staple")), "af139fa284364215adfa49c889ab7feddc5e5d1c52512ffb2cfc9baeb67f220e") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("pysodium")), "0a53ef9bc1bea173118a42bbbe8300abb6bbef83139046940e9593d9559a5df7")
def test_crypto_hash_sha256(self): self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("test")), "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("howdy")), "0f1128046248f83dc9b9ab187e16fad0ff596128f1524d05a9a77c4ad932f10a") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("Correct Horse Battery Staple")), "af139fa284364215adfa49c889ab7feddc5e5d1c52512ffb2cfc9baeb67f220e") self.assertEqual(self.byteHashToString(pysodium.crypto_hash_sha256("pysodium")), "0a53ef9bc1bea173118a42bbbe8300abb6bbef83139046940e9593d9559a5df7")
def hash_data(cls, data: Optional[bytes]) -> "Hash": """Calculates a hash of the provided bytes sequence and returns a Hash object. If `None` is provided, a hash of the empty sequence will be returned.""" if data is not None: hash_bytes = crypto_hash_sha256(data) else: hash_bytes = crypto_hash_sha256(bytes()) return cls(hash_bytes)
def test_hash(self) -> None: """Tests the Hash class.""" raw_hash = bytes([0xAB for _ in range(_HASH_BYTES_LEN)]) hash_obj = Hash(raw_hash) self.assertTrue(isinstance(hash_obj, _FixedByteArray)) self.assertEqual(hash_obj.value, raw_hash) self.assertEqual(hash_obj.hex(), raw_hash.hex()) self.assertEqual(Hash.hash_data(bytes()), Hash(crypto_hash_sha256(bytes()))) self.assertEqual(Hash.hash_data(bytes([1, 2])), Hash(crypto_hash_sha256(bytes([1, 2]))))
def test_sha2(): """ Used pysoidium.crypto_hash_sha256(message) """ # create keypair without seed verkey, sigkey = pysodium.crypto_sign_keypair() assert len(verkey) == 32 == pysodium.crypto_sign_PUBLICKEYBYTES assert len(sigkey) == 64 == pysodium.crypto_sign_SECRETKEYBYTES verkey = b'Z\x80s\x81\xd3\xf4\xaa\x94\x80\x86\x9bH\x8ay\xc2\xf9\x89k_\x946\xf1_`\x8c\xa9\xd8\xd2b\xe4\x00\x08' # digest of publickey digest = pysodium.crypto_hash_sha256(verkey) assert len(digest) == 32 == pysodium.crypto_generichash_BYTES assert digest == ( b'\x81\xce\x15L\x8b3\xb1mI\x9bF\xd9(\x0e\x99\x08SH6\xb9\xb5)\xf6\x93\xd0\x7f\x85\xe1r\xa7\x13\xd7' ) digestbig = pysodium.crypto_hash_sha512(verkey) assert len(digestbig) == 64 # assert digestbig[:32] == digest # not true for sha256 sha512 dig = hashlib.sha256(verkey).digest() assert len(dig) == 32 assert dig == digest digbig = hashlib.sha512(verkey).digest() assert len(digbig) == 64 assert digbig == digestbig """
def crack(self, ip, sealed): '''sanity check the values in the nut Because issued nuts are not stored by the server, we cannot prevent replay attacks at this point, but the timestamp limits the window of opportunity ''' try: nut = self.open(sealed) # we are getting back new nuts, check to expire an old verifier except ValueError: if self.old: return self.old.crack(ip, sealed) else: raise if self.old and self.old.expired(): self.old = None if len(ip) > 4: # IPV6 might give an attacker enough room to force a collision # of the first 4 bytes of a SHA2 hash ip = na.crypto_hash_sha256(ip)[4:] typematch = (nut.flags & NUT_IPV6) != 0 else: typematch = (nut.flags & NUT_IPV6) == 0 ipmatch = typematch and ip == nut.ip now = int(time.time()) up = int(time.monotonic()) goodtime = (nut.now >= self.start_now and now <= nut.now + self.timeout and nut.now <= self._lastnow and nut.up >= self.start_up and up <= nut.up + self.timeout) return nut, ipmatch, goodtime
def __init__(self, nodeID, kp, kc, config=None, cryptokey=None, provisioners=None): super().__init__(nodeID, kp, kc, config, cryptokey) if (not ('enable_auto_commit' in self._kc.config) or self._kc.config['enable_auto_commit'] != False): self._logger.warning("Auto commit not disabled, controller may miss messages.") if (self._kc.config['group_id'] is None): self._logger.warning("Group ID not set, controller may miss messages.") if (provisioners is None): # attempt legacy provisioners load provs = self._cryptostore.load_section('provisioners',defaults=False) if provs!=None: for p in provs: k = pysodium.crypto_hash_sha256(provs[p]) self._cryptostore.store_value(k,provs[p],section='allowlist') self._cryptostore.store_value(p,None,section='provisioners') allowlist = self._cryptostore.load_section('allowlist',defaults=False) if not (allowlist is None): allowlist = allowlist.values() denylist = self._cryptostore.load_section('denylist',defaults=False) if not (denylist is None): denylist = denylist.values() provisioners = Provisioners(allowlist=allowlist, denylist=denylist) if (not hasattr(provisioners, 'reencrypt_request') or not inspect.isroutine(provisioners.reencrypt_request)): raise KafkaCryptoControllerError("Invalid provisioners source supplied!") self._provisioners = provisioners self._last_subscribed_time = 0 self._mgmt_thread = Thread(target=self._process_mgmt_messages,daemon=True) self._mgmt_thread.start()
def encrypt_keys(self, keyidxs, keys, topic, msgval=None): if (isinstance(topic,(bytes,bytearray))): self._logger.debug("passed a topic in bytes (should be string)") topic = topic.decode('utf-8') # # msgval should be a msgpacked chain. # The public key in the array is the public key to send the key to, using a # common DH-derived key between that public key and our private encryption key. # Then there is at least one more additional item, random bytes: # (3) random bytes # Currently items after this are ignored, and reserved for future use. # try: with self.__allowdenylist_lock: pk,_ = process_chain(msgval,topic,'key-encrypt-request',allowlist=self.__allowlist,denylist=self.__denylist) # Construct shared secret as sha256(topic || random0 || random1 || our_private*their_public) epk = self.__cryptokey.get_epk(topic, 'encrypt_keys') pks = [pk[2]] eks = self.__cryptokey.use_epk(topic, 'encrypt_keys',pks) ek = eks[0] eks[0] = epk random0 = pk[3] random1 = pysodium.randombytes(self.__randombytes) ss = pysodium.crypto_hash_sha256(topic.encode('utf-8') + random0 + random1 + ek)[0:pysodium.crypto_secretbox_KEYBYTES] nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES) # encrypt keys and key indexes (MAC appended, nonce prepended) msg = [] for i in range(0,len(keyidxs)): msg.append(keyidxs[i]) msg.append(keys[i]) msg = msgpack.packb(msg, use_bin_type=True) msg = nonce + pysodium.crypto_secretbox(msg,nonce,ss) # this is then put in a msgpack array with the appropriate max_age, poison, and public key(s) poison = msgpack.packb([['topics',[topic]],['usages',['key-encrypt']]], use_bin_type=True) msg = msgpack.packb([time()+self.__maxage,poison,eks,[random0,random1],msg], use_bin_type=True) # and signed with our signing key msg = self.__cryptokey.sign_spk(msg) # and finally put as last member of a msgpacked array chaining to ROT with self.__spk_chain_lock: tchain = self.__spk_chain.copy() if (len(tchain) == 0): poison = msgpack.packb([['topics',[topic]],['usages',['key-encrypt']],['pathlen',1]], use_bin_type=True) lastcert = msgpack.packb([time()+self.__maxage,poison,self.__cryptokey.get_spk()], use_bin_type=True) _,tempsk = pysodium.crypto_sign_seed_keypair(unhexlify(b'4c194f7de97c67626cc43fbdaf93dffbc4735352b37370072697d44254e1bc6c')) tchain.append(pysodium.crypto_sign(lastcert,tempsk)) provision = msgpack.packb([msgpack.packb([0,b'\x90',self.__cryptokey.get_spk()]),self.__cryptokey.sign_spk(lastcert)], use_bin_type=True) self._logger.warning("Current signing chain is empty. Use %s to provision access and then remove temporary root of trust from allowedlist.", provision.hex()) tchain.append(msg) msg = msgpack.packb(tchain, use_bin_type=True) except Exception as e: self._logger.warning("".join(format_exception_shim(e))) return None return msg
def __init__(self, password): if (isinstance(password,(str,))): password = password.encode('utf-8') self._salt = pysodium.crypto_hash_sha256(b'Root of Trust' + password)[0:pysodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES] print("") print("Deriving root key with:") print(" opsl = ", self._ops) print(" meml = ", self._mem) print(" salt = ", hexlify(self._salt)) self._seed = pysodium.crypto_pwhash_scryptsalsa208sha256(pysodium.crypto_sign_SEEDBYTES, password, self._salt, opslimit=self._ops, memlimit=self._mem) self._pk, self._sk = pysodium.crypto_sign_seed_keypair(self._seed) print(" Root Public Key: ", hexlify(self._pk)) print("")
def __init__(self, password, rot): if (isinstance(password,(str,))): password = password.encode('utf-8') try: rot = unhexlify(rot) except: pass self._salt = {} self._salt['producer'] = pysodium.crypto_hash_sha256(b'producer' + rot)[0:pysodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES] self._salt['consumer'] = pysodium.crypto_hash_sha256(b'consumer' + rot)[0:pysodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES] self._salt['prodcon'] = pysodium.crypto_hash_sha256(b'prodcon' + rot)[0:pysodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES] self._seed = {} self._pk = {} self._sk = {} print("") print("Deriving provisioning keys with:") print(" opsl = ", self._ops) print(" meml = ", self._mem) for key in self._salt.keys(): print(" salt (", key, ") = ", hexlify(self._salt[key])) self._seed[key] = pysodium.crypto_pwhash_scryptsalsa208sha256(pysodium.crypto_sign_SEEDBYTES, password, self._salt[key], opslimit=self._ops, memlimit=self._mem) self._pk[key], self._sk[key] = pysodium.crypto_sign_seed_keypair(self._seed[key]) print(" Signing Public Key (", key, "): ", hexlify(self._pk[key])) print("")
def decrypt_keys(self, topic, msgval=None): if (isinstance(topic,(bytes,bytearray))): self._logger.debug("passed a topic in bytes (should be string)") topic = topic.decode('utf-8') # # msgval should be a msgpacked chain. # The public key in the array is a set of public key(s) to combine with our # encryption key to get the secret to decrypt the key. If we do not # have an encryption key for the topic, we cannot do it until we have one. # The next item is then the pair of random values for generating the shared # secret, followed by the actual key message. # try: with self.__allowdenylist_lock: pk,pkprint = process_chain(msgval,topic,'key-encrypt',allowlist=self.__allowlist,denylist=self.__denylist) if (len(pk) < 5): if not (pkprint is None): raise ProcessChainError("Unexpected number of chain elements:", pkprint) else: raise ValueError("Unexpected number of chain elements!") random0 = pk[3][0] random1 = pk[3][1] nonce = pk[4][0:pysodium.crypto_secretbox_NONCEBYTES] msg = pk[4][pysodium.crypto_secretbox_NONCEBYTES:] eks = self.__cryptokey.use_epk(topic, 'decrypt_keys', pk[2], clear=False) for ck in eks: # Construct candidate shared secrets as sha256(topic || random0 || random1 || our_private*their_public) ss = pysodium.crypto_hash_sha256(topic.encode('utf-8') + random0 + random1 + ck)[0:pysodium.crypto_secretbox_KEYBYTES] # decrypt and return key try: msg = msgpack.unpackb(pysodium.crypto_secretbox_open(msg,nonce,ss),raw=True) rvs = {} for i in range(0,len(msg),2): rvs[msg[i]] = msg[i+1] if len(rvs) < 1 or 2*len(rvs) != len(msg): raise ValueError except: pass else: # clear the esk/epk we just used self.__cryptokey.use_epk(topic, 'decrypt_keys', []) return rvs raise ValueError except Exception as e: self._logger.warning("".join(format_exception_shim(e))) pass return None
def get_key_value_generators(self, topic, node=None): if (isinstance(topic,(str))): topic = bytes(topic, 'utf-8') elif isinstance(topic, (bytes,bytearray)): self._logger.debug('topic provided as bytes (should be string)') # but we need it as bytes for pysodium if not (node is None) and isinstance(node,(str)): node = bytes(node, 'utf-8') # node can be provided as bytes or string in proper usage # pysodium silently computes the hash of an empty string if input is not bytes, so check for # and catch that. if (not isinstance(topic, (bytes,bytearray))): raise KafkaCryptoRatchetError("Topic is not bytes!") hash = pysodium.crypto_hash_sha256(topic) # generate per topic key key,_ = self.generate(salt=hash[0:self.SALTSIZE],ctx=hash[self.SALTSIZE:],keysize=self.SECRETSIZE,noncesize=0) ki = self.__keyidx if node is not None: ki = ki.to_bytes(16, byteorder='big') ki = pysodium.crypto_generichash(node + ki) kg, vg = KeyGenerator.get_key_value_generators(key) return (ki, key, kg, vg)
def crypt_aed_decrypt(data, keystr): key = binascii.unhexlify(binary_to_hex(pysodium.crypto_hash_sha256(keystr))) nonce = binascii.unhexlify(b"cd7cf67be39c7977") ad = "" rtn = pysodium.crypto_aead_chacha20poly1305_decrypt(data, ad, nonce, key) return rtn
def _process_mgmt_messages(self): while True: # First, process messages # we are the only thread ever using _kc, _kp, so we do not need the lock to use them msgs = self._kc.poll(timeout_ms=self.MGMT_POLL_INTERVAL, max_records=self.MGMT_POLL_RECORDS) # but to actually process messages, we need the lock for tp,msgset in msgs.items(): self._lock.acquire() for msg in msgset: topic = msg.topic if (isinstance(topic,(bytes,bytearray))): self._logger.debug("passed a topic in bytes (should be string)") topic = topic.decode('utf-8') self._tps_offsets[topic] = msg.offset+1 self._logger.debug("Processing message: %s", msg) if topic[-len(self.TOPIC_SUFFIX_REQS):] == self.TOPIC_SUFFIX_REQS: root = topic[:-len(self.TOPIC_SUFFIX_REQS)] # A new receiver: send all requested keys try: kreq = msgpack.unpackb(msg.key,raw=True) if root in self._pgens.keys(): ki = [] s = [] for ski,sk in self._pgens[root].items(): if ski in kreq: ki.append(ski) s.append(sk['secret']) if len(ki) > 0: k = msgpack.packb(ki, use_bin_type=True) v = self._cryptoexchange.encrypt_keys(ki, s, root, msgval=msg.value) if not (v is None): self._logger.info("Sending current encryption keys for root=%s to new receiver, msgkey=%s.", root, k) self._kp.send(root + self.TOPIC_SUFFIX_KEYS, key=k, value=v) else: self._logger.info("Failed sending current encryption keys for root=%s to new receiver.", root) else: self._logger.info("No keys for root=%s to send to new receiver.", root) except Exception as e: self._parent._logger.warning("".join(traceback.format_exception(etype=type(e), value=e, tb=e.__traceback__))) elif topic[-len(self.TOPIC_SUFFIX_KEYS):] == self.TOPIC_SUFFIX_KEYS: root = topic[:-len(self.TOPIC_SUFFIX_KEYS)] # New key(s) nks = self._cryptoexchange.decrypt_keys(root,msgval=msg.value) if not (nks is None): for nki,nk in nks.items(): self._logger.info("Received new encryption key for root=%s, key index=%s, msgkey=%s", root, nki, msg.key) # do not clopper other keys that may exist if not (root in self._cgens.keys()): self._cgens[root] = {} # but do clobber the same topic,keyID entry self._cgens[root][nki] = {} self._cgens[root][nki]['key'], self._cgens[root][nki]['value'] = KeyGenerator.get_key_value_generators(nk) self._cgens[root][nki]['secret'] = nk # now that we have this key index, clear from request lists if root in self._cwaits.keys(): self._cwaits[root].pop(nki, None) if root in self._subs_last: eki = set(self._subs_last[root][1]) eki.difference_update(set(nks.keys())) if len(eki) > 0: self._logger.warning("For root=%s, the keys %s were requested but not received.", root, eki) self._subs_last.pop(root,None) elif topic == self.MGMT_TOPIC_CHAINS: # New candidate public key chain self._logger.info("Received new chain message: %s", msg) if msg.key == self._cryptokey.get_spk(): self._logger.debug("Key matches ours. Validating Chain.") newchain = self._cryptoexchange.replace_spk_chain(msg.value) if not (newchain is None): self._logger.info("New chain is superior, using it.") self._cryptostore.store_value('chain',newchain,section='crypto') elif topic == self.MGMT_TOPIC_ALLOWLIST: self._logger.info("Received new allowlist message: %s", msg) allow = self._cryptoexchange.add_allowlist(msg.value) if not (allow is None): c = pysodium.crypto_hash_sha256(allow) self._cryptostore.store_value(c,allow,section='allowlist') elif topic == self.MGMT_TOPIC_DENYLIST: self._logger.info("Received new denylist message: %s", msg) deny = self._cryptoexchange.add_denylist(msg.value) if not (deny is None): c = pysodium.crypto_hash_sha256(deny) self._cryptostore.store_value(c,deny,section='denylist') else: # unknown object log_limited(self._logger.warning, "Unknown topic type in message: %s", msg) self._lock.release() # Flush producer self._kp.flush() # Second, deal with subscription changes self._lock.acquire() self._logger.debug("Processing subscription changes.") if self._tps_updated == True: self._logger.debug("Subscriptions changed, adjusting.") tpo = [] for tk in self._tps.keys(): tpo.append(TopicPartitionOffset(self._tps[tk].topic, self._tps[tk].partition, self._tps_offsets[tk])) self._kc.assign_and_seek(tpo) self._logger.debug("Subscriptions adjusted.") if (self._kc.config['group_id'] is None): self._logger.info("No group_id, seeking to beginning.") self._kc.seek_to_beginning() for tk in self._tps.keys(): if (tk[-len(self.TOPIC_SUFFIX_KEYS):] == self.TOPIC_SUFFIX_KEYS): root = tk[:-len(self.TOPIC_SUFFIX_KEYS)] if not (root in self._subs_needed): self._subs_needed.append(root) self._tps_updated = False self._lock.release() # Third, deal with topics needing subscriptions self._lock.acquire() self._logger.debug("Process subscriptions.") subs_needed_next = [] for root in self._subs_needed: if root in self._cwaits.keys(): self._logger.debug("Attempting (Re)subscribe to root=%s", root) kis = list(self._cwaits[root].keys()) if len(kis) > 0: if (not (root in self._subs_last.keys()) or self._subs_last[root][0]+self.CRYPTO_SUB_INTERVAL<time()): k = msgpack.packb(kis, use_bin_type=True) v = self._cryptoexchange.signed_epk(root) if not (k is None) and not (v is None): self._logger.info("Sending new subscribe request for root=%s, msgkey=%s", root, k) self._kp.send(root + self.TOPIC_SUFFIX_SUBS, key=k, value=v) if not self._cryptoexchange.valid_spk_chain(): # if using default/temp ROT, send directly as well self._kp.send(root + self.TOPIC_SUFFIX_REQS, key=k, value=v) self._subs_last[root] = [time(),kis] else: self._logger.info("Failed to send new subscribe request for root=%s", root) subs_needed_next.append(root) else: self._logger.debug("Deferring (re)subscriptions for root=%s due to pending key request.", root) subs_needed_next.append(root) else: self._logger.debug("No new keys needed for root=%s", root) self._subs_needed = subs_needed_next self._lock.release() # Flush producer self._kp.flush() # Fourth, periodically increment ratchet and prune old keys self._logger.debug("Checking ratchet time.") self._lock.acquire() if self._last_time+self.CRYPTO_RATCHET_INTERVAL < time(): self._logger.info("Periodic ratchet increment.") self._last_time = time() # prune for root,pgens in self._pgens.items(): rki = [] for ki,kv in pgens.items(): if (kv['birth']+self.CRYPTO_MAX_PGEN_AGE<time()): rki.append(ki) for ki in rki: self._pgens[root].pop(ki) # increment self._cur_pgens = {} self._seed.increment() self._lock.release() # Fifth, write new producer keys if requested self._logger.debug("Checking producer keys.") self._lock.acquire() if self._new_pgens: self._logger.info("(Re)writing producer keys.") self._new_pgens = False kvs = {} kvs['pgens'] = [] for root,pgens in self._pgens.items(): for ki,kv in pgens.items(): kvs['pgens'].append([root,ki,kv['secret'],kv['birth']]) # stored pgens do not have generators as they should never be used for active production # (but secret stays around so lost consumers can catch up) self._logger.info("Saving %s old production keys.", len(kvs['pgens'])) self._cryptostore.store_opaque_value('oldkeys',msgpack.packb(kvs, use_bin_type=True),section="crypto") self._lock.release()
def _process_mgmt_messages(self): while True: # First, (Re)subscribe if needed if ((time() - self._last_subscribed_time) >= self.MGMT_SUBSCRIBE_INTERVAL): self._logger.debug("Initiating resubscribe...") trx = "(.*\\" + self.TOPIC_SUFFIX_SUBS + "$)" self._kc.subscribe(topics=[ self.MGMT_TOPIC_CHAINS, self.MGMT_TOPIC_ALLOWLIST, self.MGMT_TOPIC_DENYLIST ], pattern=trx) self._last_subscribed_time = time() self._logger.info("Resubscribed to topics.") # Second, process messages # we are the only thread ever using _kc, _kp, so we do not need the lock to use them self._logger.debug("Initiating poll...") msgs = self._kc.poll(timeout_ms=self.MGMT_POLL_INTERVAL, max_records=self.MGMT_POLL_RECORDS) self._logger.debug("Poll complete with %i msgsets.", len(msgs)) # but to actually process messages, we need the lock for tp, msgset in msgs.items(): self._logger.debug("Topic %s Partition %i has %i messages", tp.topic, tp.partition, len(msgset)) self._lock.acquire() for msg in msgset: self._logger.debug("Processing message: %s", msg) topic = msg.topic if (isinstance(topic, (bytes, bytearray))): self._logger.debug( "topic provided as bytes instead of string") topic = topic.decode('utf-8') if topic[-len(self.TOPIC_SUFFIX_SUBS ):] == self.TOPIC_SUFFIX_SUBS: root = topic[:-len(self.TOPIC_SUFFIX_SUBS)] self._logger.debug( "Processing subscribe message, root=%s, msgkey=%s", root, msg.key) # New consumer encryption key. Validate k, v = self._provisioners.reencrypt_request( root, cryptoexchange=self._cryptoexchange, msgkey=msg.key, msgval=msg.value) # Valid request, resign and retransmit if (not (k is None)) or (not (v is None)): self._logger.info( "Valid consumer key request on topic=%s, root=%s, msgkey=%s. Resending to topic=%s, msgkey=%s", topic, root, msg.key, root + self.TOPIC_SUFFIX_REQS, k) self._kp.send(root + self.TOPIC_SUFFIX_REQS, key=k, value=v) else: self._logger.info( "Invalid consumer key request on topic=%s, root=%s in message: %s", topic, root, msg) elif topic == self.MGMT_TOPIC_CHAINS: # New candidate public key chain self._logger.info("Received new chain message: %s", msg) if msg.key == self._cryptokey.get_spk(): self._logger.debug( "Key matches ours. Validating Chain.") newchain = self._cryptoexchange.replace_spk_chain( msg.value) if not (newchain is None): self._logger.info( "New chain is superior, using it.") self._cryptostore.store_value('chain', newchain, section='crypto') elif topic == self.MGMT_TOPIC_ALLOWLIST: self._logger.info("Received new allowlist message: %s", msg) allow = self._cryptoexchange.add_allowlist(msg.value) if not (allow is None): c = pysodium.crypto_hash_sha256(allow) self._cryptostore.store_value(c, allow, section='allowlist') elif topic == self.MGMT_TOPIC_DENYLIST: self._logger.info("Received new denylist message: %s", msg) deny = self._cryptoexchange.add_denylist(msg.value) if not (deny is None): c = pysodium.crypto_hash_sha256(deny) self._cryptostore.store_value(c, deny, section='denylist') else: # unknown object log_limited(self._logger.warning, "Unknown topic type in message: %s", msg) self._lock.release() # Third, flush producer self._kp.flush() # Fourth, commit offsets if (self._kc.config['group_id'] is not None): self._kc.commit()