Пример #1
0
    def testWireFormatAccess(self):

        m = rdf_flows.SignedMessageList()

        now = 1369308998000000

        # An unset RDFDatetime with no defaults will be None.
        self.assertEqual(m.timestamp, None)

        # Set the wireformat to the integer equivalent.
        m.SetPrimitive("timestamp", now)

        self.assertTrue(isinstance(m.timestamp, rdfvalue.RDFDatetime))
        self.assertEqual(m.timestamp, now)

        rdf_now = rdfvalue.RDFDatetime().Now()

        m.timestamp = rdf_now
        self.assertEqual(m.GetPrimitive("timestamp"), int(rdf_now))
Пример #2
0
    def DecodeMessages(self, response_comms):
        """Extract and verify server message.

    Args:
        response_comms: A ClientCommunication rdfvalue

    Returns:
       list of messages and the CN where they came from.

    Raises:
       DecryptionError: If the message failed to decrypt properly.
    """
        if response_comms.api_version not in [3]:
            raise DecryptionError("Unsupported api version: %s, expected 3." %
                                  response_comms.api_version)

        if response_comms.encrypted_cipher:
            # Have we seen this cipher before?
            try:
                cipher = self.encrypted_cipher_cache.Get(
                    response_comms.encrypted_cipher)
            except KeyError:
                cipher = ReceivedCipher(response_comms, self.private_key,
                                        self.pub_key_cache)

                if cipher.signature_verified:
                    # Remember it for next time.
                    self.encrypted_cipher_cache.Put(
                        response_comms.encrypted_cipher, cipher)

            # Verify the cipher HMAC with the new response_comms. This will raise
            # DecryptionError if the HMAC does not agree.
            cipher.VerifyHMAC(response_comms)

            # Decrypt the message with the per packet IV.
            plain = cipher.Decrypt(response_comms.encrypted,
                                   response_comms.packet_iv)
            try:
                signed_message_list = rdf_flows.SignedMessageList(plain)
            except rdfvalue.DecodeError as e:
                raise DecryptionError(str(e))

            message_list = self.DecompressMessageList(signed_message_list)

        else:
            # The message is not encrypted. We do not allow unencrypted
            # messages:
            raise DecryptionError("Server response is not encrypted.")

        # Are these messages authenticated?
        auth_state = self.VerifyMessageSignature(response_comms,
                                                 signed_message_list, cipher,
                                                 response_comms.api_version)

        # Mark messages as authenticated and where they came from.
        for msg in message_list.job:
            msg.auth_state = auth_state
            msg.source = cipher.cipher_metadata.source

        return (message_list.job, cipher.cipher_metadata.source,
                signed_message_list.timestamp)
Пример #3
0
  def EncodeMessages(self,
                     message_list,
                     result,
                     destination=None,
                     timestamp=None,
                     api_version=3):
    """Accepts a list of messages and encodes for transmission.

    This function signs and then encrypts the payload.

    Args:
       message_list: A MessageList rdfvalue containing a list of
       GrrMessages.

       result: A ClientCommunication rdfvalue which will be filled in.

       destination: The CN of the remote system this should go to.

       timestamp: A timestamp to use for the signed messages. If None - use the
              current time.

       api_version: The api version which this should be encoded in.

    Returns:
       A nonce (based on time) which is inserted to the encrypted payload. The
       client can verify that the server is able to decrypt the message and
       return the nonce.

    Raises:
       RuntimeError: If we do not support this api version.
    """
    if api_version not in [3]:
      raise RuntimeError(
          "Unsupported api version: %s, expected 3." % api_version)
    # TODO(user): This is actually not great, we have two
    # communicator classes already, one for the client, one for the
    # server. This should be different methods, not a single one that
    # gets passed a destination (server side) or not (client side).
    if destination is None:
      destination = self.server_name
      # For the client it makes sense to cache the server cipher since
      # it's the only cipher it ever uses.
      cipher = self._GetServerCipher()
    else:
      remote_public_key = self._GetRemotePublicKey(destination)
      cipher = Cipher(self.common_name, self.private_key, remote_public_key)

    # Make a nonce for this transaction
    if timestamp is None:
      self.timestamp = timestamp = long(time.time() * 1000000)

    signed_message_list = rdf_flows.SignedMessageList(timestamp=timestamp)
    self.EncodeMessageList(message_list, signed_message_list)

    result.encrypted_cipher_metadata = cipher.encrypted_cipher_metadata

    # Include the encrypted cipher.
    result.encrypted_cipher = cipher.encrypted_cipher

    serialized_message_list = signed_message_list.SerializeToString()

    # Encrypt the message symmetrically.
    # New scheme cipher is signed plus hmac over message list.
    result.packet_iv, result.encrypted = cipher.Encrypt(serialized_message_list)

    # This is to support older endpoints.
    result.hmac = cipher.HMAC(result.encrypted)

    # Newer endpoints only look at this HMAC. It is recalculated for each packet
    # in the session. Note that encrypted_cipher and encrypted_cipher_metadata
    # do not change between all packets in this session.
    result.full_hmac = cipher.HMAC(result.encrypted, result.encrypted_cipher,
                                   result.encrypted_cipher_metadata,
                                   result.packet_iv.SerializeToString(),
                                   struct.pack("<I", api_version))

    result.api_version = api_version

    if isinstance(result, rdfvalue.RDFValue):
      # Store the number of messages contained.
      result.num_messages = len(message_list)

    return timestamp
Пример #4
0
    def EncodeMessages(self,
                       message_list,
                       result,
                       destination=None,
                       timestamp=None,
                       api_version=3):
        """Accepts a list of messages and encodes for transmission.

    This function signs and then encrypts the payload.

    Args:
       message_list: A MessageList rdfvalue containing a list of
       GrrMessages.

       result: A ClientCommunication rdfvalue which will be filled in.

       destination: The CN of the remote system this should go to.

       timestamp: A timestamp to use for the signed messages. If None - use the
              current time.

       api_version: The api version which this should be encoded in.

    Returns:
       A nonce (based on time) which is inserted to the encrypted payload. The
       client can verify that the server is able to decrypt the message and
       return the nonce.

    Raises:
       RuntimeError: If we do not support this api version.
    """
        if api_version not in [3]:
            raise RuntimeError("Unsupported api version: %s, expected 3." %
                               api_version)

        if destination is None:
            destination = self.server_name

        # Make a nonce for this transaction
        if timestamp is None:
            self.timestamp = timestamp = long(time.time() * 1000000)

        # Do we have a cached cipher to talk to this destination?
        try:
            cipher = self.cipher_cache.Get(destination)

        except KeyError:
            # Make a new one
            cipher = Cipher(self.common_name, destination, self.private_key,
                            self.pub_key_cache)
            self.cipher_cache.Put(destination, cipher)

        signed_message_list = rdf_flows.SignedMessageList(timestamp=timestamp)
        self.EncodeMessageList(message_list, signed_message_list)

        result.encrypted_cipher_metadata = cipher.encrypted_cipher_metadata

        # Include the encrypted cipher.
        result.encrypted_cipher = cipher.encrypted_cipher

        serialized_message_list = signed_message_list.SerializeToString()

        # Encrypt the message symmetrically.
        # New scheme cipher is signed plus hmac over message list.
        result.packet_iv, result.encrypted = cipher.Encrypt(
            serialized_message_list)

        # This is to support older endpoints.
        result.hmac = cipher.HMAC(result.encrypted)

        # Newer endpoints only look at this HMAC. It is recalculated for each packet
        # in the session. Note that encrypted_cipher and encrypted_cipher_metadata
        # do not change between all packets in this session.
        result.full_hmac = cipher.HMAC(result.encrypted,
                                       result.encrypted_cipher,
                                       result.encrypted_cipher_metadata,
                                       result.packet_iv,
                                       struct.pack("<I", api_version))

        result.api_version = api_version

        if isinstance(result, rdfvalue.RDFValue):
            # Store the number of messages contained.
            result.num_messages = len(message_list)

        return timestamp
Пример #5
0
    def DecodeMessages(self, response_comms):
        """Extract and verify server message.

    Args:
        response_comms: A ClientCommunication rdfvalue

    Returns:
       list of messages and the CN where they came from.

    Raises:
       DecryptionError: If the message failed to decrypt properly.
    """
        # Have we seen this cipher before?
        cipher_verified = False
        try:
            cipher = self.encrypted_cipher_cache.Get(
                response_comms.encrypted_cipher)
            # Even though we have seen this encrypted cipher already, we should still
            # make sure that all the other fields are sane and verify the HMAC.
            cipher.VerifyReceivedHMAC(response_comms)
            cipher_verified = True

            # If we have the cipher in the cache, we know the source and
            # should have a corresponding public key.
            source = cipher.GetSource()
            remote_public_key = self._GetRemotePublicKey(source)
        except KeyError:
            cipher = ReceivedCipher(response_comms, self.private_key)

            source = cipher.GetSource()
            try:
                remote_public_key = self._GetRemotePublicKey(source)
                if cipher.VerifyCipherSignature(remote_public_key):
                    # At this point we know this cipher is legit, we can cache it.
                    self.encrypted_cipher_cache.Put(
                        response_comms.encrypted_cipher, cipher)
                    cipher_verified = True

            except UnknownClientCert:
                # We don't know who we are talking to.
                remote_public_key = None

        # Decrypt the message with the per packet IV.
        plain = cipher.Decrypt(response_comms.encrypted,
                               response_comms.packet_iv)
        try:
            signed_message_list = rdf_flows.SignedMessageList(plain)
        except rdfvalue.DecodeError as e:
            raise DecryptionError(str(e))

        message_list = self.DecompressMessageList(signed_message_list)

        # Are these messages authenticated?
        auth_state = self.VerifyMessageSignature(response_comms,
                                                 signed_message_list, cipher,
                                                 cipher_verified,
                                                 response_comms.api_version,
                                                 remote_public_key)

        # Mark messages as authenticated and where they came from.
        for msg in message_list.job:
            msg.auth_state = auth_state
            msg.source = cipher.cipher_metadata.source

        return (message_list.job, cipher.cipher_metadata.source,
                signed_message_list.timestamp)