def test_serialize_from_objects(self): """ Tests that serialize/deserialize are inverse operations with from_objects factory function """ sk, vk = W.new() message = self._default_msg() env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) self.assertEqual(env, Envelope.from_bytes(env.serialize()))
def _validate_envelope(self, envelope_binary: bytes, header: str) -> Union[None, Envelope]: # TODO return/raise custom exceptions in this instead of just logging stuff and returning none # Deserialize envelope env = None try: env = Envelope.from_bytes(envelope_binary) except Exception as e: self.log.error("\n\n\nError deserializing envelope: {}\n\n\n".format(e)) return None # Check seal if not env.verify_seal(): self.log.error("\n\n\nSeal could not be verified for envelope {}\n\n\n".format(env)) return None # If header is not none (meaning this is a ROUTE msg with an ID frame), then verify that the ID frame is # the same as the vk on the seal if header and (header != env.seal.verifying_key): self.log.error("\n\n\nHeader frame {} does not match seal's vk {}\nfor envelope {}\n\n\n" .format(header, env.seal.verifying_key, env)) return None # Make sure we haven't seen this message before if env.meta.uuid in Executor._recently_seen: self.log.warning("Duplicate envelope detect with UUID {}. Ignoring.".format(env.meta.uuid)) return None # TODO -- checks timestamp to ensure this envelope is recv'd in a somewhat reasonable time (within N seconds) # If none of the above checks above return None, this envelope should be good return env
def create(cls, envelope: Envelope = None, envelope_binary: bytes = None, **kwargs): assert not ( envelope and envelope_binary ), "Either envelope or envelope_binary should be passed in (not both)" cmd = reactor_capnp.ReactorCommand.new_message() if envelope: assert isinstance( envelope, Envelope), "'envelope' kwarg must be an Envelope instance" cmd.envelope.data = envelope.serialize() if envelope_binary: assert isinstance(envelope_binary, bytes), "'envelope_binary' must be bytes" cmd.envelope.data = envelope_binary cmd.init('kwargs', len(kwargs)) for i, key in enumerate(kwargs): cmd.kwargs[i].key, cmd.kwargs[i].value = str(key), str(kwargs[key]) return cls.from_data(cmd)
def run(self): count = 0 while count < NUM_SENDS: time.sleep(SEND_RATE) self.log.critical("Stu sending stab") stab = Stab.from_data("stab #{}".format(count)) self.parent.reactor.pub(url=URL2, data=Envelope.create(stab)) count += 1
def send_pub(self, filter: str, envelope: bytes): assert isinstance(filter, str), "'id' arg must be a string" assert isinstance(envelope, bytes), "'envelope' arg must be bytes" assert len(self.pubs) > 0, "Attempted to publish data but publisher socket(s) is not configured" for url in self.pubs: self.log.debug("Publishing to URL {} with envelope: {}".format(url, Envelope.from_bytes(envelope))) # self.log.info("Publishing to... {}".format(url)) self.pubs[url].send_multipart([filter.encode(), envelope])
def _package_msg(self, msg: MessageBase) -> Envelope: """ Convenience method to package a message into an envelope :param msg: The MessageBase instance to package :return: An Envelope instance """ assert type(msg) is not Envelope, "Attempted to package a 'message' that is already an envelope" assert issubclass(type(msg), MessageBase), "Attempted to package a message that is not a MessageBase subclass" return Envelope.create_from_message(message=msg, signing_key=self.signing_key, verifying_key=self.verifying_key)
def recv_poke_req(self, poke: PokeRequest, id): self.log.critical("Stu got poke {}, but waiting {} seconds...".format(REPLY_WAIT)) time.sleep(REPLY_WAIT) self.log.critical("Stu replying to poke <{}> with id {}".format(poke, id)) reply = PokeReply.from_data("yoyo this is my reply to {}".format(poke)) stab = Stab.from_data("stab!") self.parent.reactor.pub(url=URL2, data=Envelope.create(stab)) return reply
def _recv_reply_env(self, header: str, envelope: Envelope): self.log.debug("Recv REPLY envelope with header {} and envelope {}".format(header, envelope)) reply_uuid = envelope.meta.uuid if reply_uuid in self.expected_replies: self.log.debug("Removing reply with uuid {} from expected replies".format(reply_uuid)) self.expected_replies[reply_uuid].cancel() del(self.expected_replies[reply_uuid]) self.call_on_mp(callback=StateInput.INPUT, header=header, envelope_binary=envelope.serialize())
def test_create_with_envelope(self): """ Tests creating a message with an envelope produces an object with the expected properties """ sk, vk = ED25519Wallet.new() tx = StandardTransactionBuilder.random_tx() sender = 'me' env = Envelope.create_from_message(message=tx, signing_key=sk) cmd = ReactorCommand.create_cmd('some_cls', 'some_func', envelope=env) self.assertTrue(ReactorCommand.envelope, env)
def test_create_from_message(self): """ Tests create_from_message with valid args (no vk passed) creates an envelope valid signature and expected fields """ sk, vk = W.new() msg = self._default_msg() env = Envelope.create_from_message(message=msg, signing_key=sk) self.assertTrue(env.verify_seal()) self.assertEqual(env.message, msg) self.assertEqual(env.seal.verifying_key, vk)
def test_lazy_serialize(self): """ Tests that creating an envelope from_bytes does not try to repack the underlying struct when serialize is called. This is b/c the serialize() func should be 'cached' with the binary data passed into from_bytes """ sk, vk = W.new() message = self._default_msg() env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) env_binary = env.serialize() clone = Envelope.from_bytes(env_binary) clone._data = MagicMock() clone_binary = clone.serialize() # this should not pack the capnp struct (_data) again # The Envelope Kitchen Sink lol -- make sure no serialization related API is called clone._data.as_builder.assert_not_called() clone._data.to_bytes_packed.assert_not_called() clone._data.to_bytes.assert_not_called()
def test_create_from_objects(self): """ Test that create returns an object with expected properties """ seal = self._default_seal() meta = self._default_meta() message = self._default_msg() env = Envelope.create_from_objects(seal=seal, meta=meta, message=message.serialize()) self.assertEqual(env.seal, seal) self.assertEqual(env.meta, meta) self.assertEqual(env.message, message)
def test_validate_bad_seal(self): meta = self._default_meta() message = self._default_msg() sk, vk = W.new() sk2, vk2 = W.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk2) env = Envelope.create_from_objects(seal=seal, meta=meta, message=message.serialize()) self.assertFalse(env.verify_seal())
def route(self, msg_binary: bytes): msg = None try: envelope = Envelope.from_bytes(msg_binary) msg = envelope.open() except Exception as e: self.log.error("Error opening envelope: {}".format(e)) if type(msg) in self._receivers: self.log.debug("Routing msg: {}".format(msg)) self._receivers[type(msg)](self, msg) else: self.log.error("Message {} has no implemented receiver in {}".format(msg, self._receivers))
def test_verify_seal(self): """ Tests verify seal with a valid signature """ meta = self._default_meta() message = self._default_msg() sk, vk = W.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) self.assertTrue(env.verify_seal())
def _package_reply(self, reply: MessageBase, req_env: Envelope) -> Envelope: """ Convenience method to create a reply envelope. The difference between this func and _package_msg, is that in the reply envelope the UUID must be the hash of the original request's uuid (not some randomly generated int) :param reply: The reply message (an instance of MessageBase) :param req_env: The original request envelope (an instance of Envelope) :return: An Envelope instance """ self.log.debug("Creating REPLY envelope with msg type {} for request envelope {}".format(type(reply), req_env)) request_uuid = req_env.meta.uuid reply_uuid = EnvelopeAuth.reply_uuid(request_uuid) return Envelope.create_from_message(message=reply, signing_key=self.signing_key, verifying_key=self.verifying_key, uuid=reply_uuid)
def test_validate_envelope(self): """ Tests validate envelope function """ meta = self._default_meta() message = self._default_msg() sk, vk = W.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope.create_from_objects(seal=seal, meta=meta, message=message.serialize()) env.validate()
def test_verify_seal_invalid(self): """ Tests verifying a seal with an invalid signature """ meta = self._default_meta() message = self._default_msg() sk, vk = W.new() sk_prime = 'A' * 64 signature = EnvelopeAuth.seal(signing_key=sk_prime, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope.create_from_objects(seal=seal, meta=meta, message=message.serialize()) env.verify_seal()
def test_create_from_message_bad_keypair(self): """ test create_from_message passing in sk does and incorrect vk """ sk, vk = W.new() sk1, vk1 = W.new() msg = self._default_msg() sender = 'dat boi' env = Envelope.create_from_message(message=msg, signing_key=sk, verifying_key=vk) # no error self.assertEqual(env.seal.verifying_key, vk) self.assertRaises(Exception, Envelope.create_from_message, msg, sk, sender, vk1) self.assertNotEqual(env.seal.verifying_key, vk1)
def envelope(self): if self._data.envelope.which() == 'unset': return None else: return Envelope.from_bytes(self._data.envelope.data)
def send_poke(self, poke_msg): poke = Poke.from_data(poke_msg) evl = Envelope.create(poke) self.reactor.pub(url=self.url, data=evl.serialize())
def _recv_request_env(self, header: str, envelope: Envelope): self.log.debug("Recv REQUEST envelope with header {} and envelope {}".format(header, envelope)) self.call_on_mp(callback=StateInput.REQUEST, header=header, envelope_binary=envelope.serialize())
def _recv_pub_env(self, header: str, envelope: Envelope): self.log.debug("Recv'd pub envelope with header {} and env {}".format(header, envelope)) self.call_on_mp(callback=StateInput.INPUT, envelope_binary=envelope.serialize())