Ejemplo n.º 1
0
 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")
Ejemplo n.º 2
0
 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")
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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]))))
Ejemplo n.º 5
0
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
    """
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
  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()
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
 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("")
Ejemplo n.º 10
0
 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("")
Ejemplo n.º 11
0
 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
Ejemplo n.º 12
0
 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)
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
  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()
Ejemplo n.º 15
0
    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()