def _get_key(cls, device_id):
        """Attempt to get a user key from an environment variable
        """

        var_name = "USER_KEY_{0:08X}".format(device_id)

        if var_name not in os.environ:
            raise NotFoundError("No user key could be found for devices",
                                device_id=device_id,
                                expected_variable_name=var_name)

        key_var = os.environ[var_name]
        if len(key_var) != 64:
            raise NotFoundError(
                "User key in variable is not the correct length, should be 64 hex characters",
                device_id=device_id,
                key_value=key_var)

        try:
            key = binascii.unhexlify(key_var)
        except ValueError:
            raise NotFoundError(
                "User key in variable could not be decoded from hex",
                device_id=device_id,
                key_value=key_var)

        if len(key) != 32:
            raise NotFoundError(
                "User key in variable is not the correct length, should be 64 hex characters",
                device_id=device_id,
                key_value=key_var)

        return key
Exemple #2
0
    def verify_key(self, root_key_type):
        """Verify that the root key type is known to us.

        Args:
            root_key_type: requested key type

        Raises:
            NotFoundError: If the key type is not known.
        """

        if root_key_type not in self.KnownKeyRoots:
            raise NotFoundError("Unknown key type", key=root_key_type)

        if root_key_type not in self.supported_keys:
            raise NotFoundError("Not supported key type", key=root_key_type)
Exemple #3
0
    def sign_report(self, device_id, root, data, **kwargs):
        """Sign a buffer of report data on behalf of a device.

        Args:
            device_id (int): The id of the device that we should encrypt for
            root (int): The root key type that should be used to generate the report
            data (bytearray): The data that we should sign
            **kwargs: There are additional specific keyword args that are required
                depending on the root key used.  Typically, you must specify
                - report_id (int): The report id
                - sent_timestamp (int): The sent timestamp of the report

                These two bits of information are used to construct the per report
                signing and encryption key from the specific root key type.

        Returns:
            dict: The signature and any associated metadata about the signature.
                The signature itself must always be a bytearray stored under the
                'signature' key, however additional keys may be present depending
                on the signature method used.

        Raises:
            NotFoundError: If the auth provider is not able to sign the data.
        """

        for _priority, provider in self.providers:
            try:
                return provider.sign_report(device_id, root, data, **kwargs)
            except NotFoundError:
                pass

        raise NotFoundError(
            "sign_report method is not implemented in any sub_providers")
    def sign_report(self, device_id, root, data, **kwargs):
        """Sign a buffer of report data on behalf of a device.

        Args:
            device_id (int): The id of the device that we should encrypt for
            root (int): The root key type that should be used to generate the report
            data (bytearray): The data that we should sign
            **kwargs: There are additional specific keyword args that are required
                depending on the root key used.  Typically, you must specify
                - report_id (int): The report id
                - sent_timestamp (int): The sent timestamp of the report

                These two bits of information are used to construct the per report
                signing and encryption key from the specific root key type.

        Returns:
            dict: The signature and any associated metadata about the signature.
                The signature itself must always be a bytearray stored under the
                'signature' key, however additional keys may be present depending
                on the signature method used.

        Raises:
            NotFoundError: If the auth provider is not able to sign the data.
        """

        AuthProvider.VerifyRoot(root)

        if root != AuthProvider.NoKey:
            raise NotFoundError('unsupported root key in BasicAuthProvider',
                                root_key=root)

        result = bytearray(hashlib.sha256(data).digest())
        return {'signature': result, 'root_key': root}
    def encrypt_report(self, device_id, root, data, **kwargs):
        """Encrypt a buffer of report data on behalf of a device.

        Args:
            device_id (int): The id of the device that we should encrypt for
            root (int): The root key type that should be used to generate the report
            data (bytearray): The data that we should encrypt.
            **kwargs: There are additional specific keyword args that are required
                depending on the root key used.  Typically, you must specify
                - report_id (int): The report id
                - sent_timestamp (int): The sent timestamp of the report

                These two bits of information are used to construct the per report
                signing and encryption key from the specific root key type.

        Returns:
            dict: The encrypted data and any associated metadata about the data.
                The data itself must always be a bytearray stored under the 'data'
                key, however additional keys may be present depending on the encryption method
                used.

        Raises:
            NotFoundError: If the auth provider is not able to encrypt the data.
        """

        raise NotFoundError("encrypt_report method is not implemented")
    def get_serialized_key(self, key_type, device_id, **key_info):
        """Get a serialized key such for signing a streamer report.

        These keys are designed to only be used once and only provide
            access to the object of key_type with the given serial_number

        Args:
            key_type (int): no key, user key or device key
            device_id (int): UUID of the device
            key_info (dict): data required for key generation.
                It may be report_id and sent_timestamp

        Returns:
            bytearray: the key
        """
        report_id = key_info.get('report_id', None)
        sent_timestamp = key_info.get('sent_timestamp', None)

        if report_id is None or sent_timestamp is None:
            raise NotFoundError('report_id or sent_timestamp is not provided')

        self.verify_key(key_type)

        root_key = self.get_root_key(key_type, device_id)
        report_key = self.DeriveReportKey(root_key, report_id, sent_timestamp)

        return report_key
    def verify_report(self, device_id, root, data, signature, **kwargs):
        """Verify a buffer of report data on behalf of a device.

        Args:
            device_id (int): The id of the device that we should encrypt for
            root (int): The root key type that should be used to generate the report
            data (bytearray): The data that we should verify
            signature (bytearray): The signature attached to data that we should verify
            **kwargs: There are additional specific keyword args that are required
                depending on the root key used.  Typically, you must specify
                - report_id (int): The report id
                - sent_timestamp (int): The sent timestamp of the report

                These two bits of information are used to construct the per report
                signing and encryption key from the specific root key type.

        Returns:
            dict: The result of the verification process must always be a bool under the
                'verified' key, however additional keys may be present depending on the
                signature method used.

        Raises:
            NotFoundError: If the auth provider is not able to verify the data due to
                an error.  If the data is simply not valid, then the function returns
                normally.
        """

        raise NotFoundError("verify method is not implemented")
Exemple #8
0
    def DeriveRotatedKey(cls, reboot_key, current_timestamp,
                         rotation_interval_power):
        """Derive an ephemeral key every 2^rotation_interval_power seconds,
        The same key will be return if timeout of rotation is not elapsed

        AES-128-ECB(Reboot Key, current_timestamp with low X bits masked to 0)

        Args:
            reboot_key (bytearray): 16 bytes key
            current_timestamp (int): current time
            rotation_interval_power (int): X in 2^X

        Returns:
            bytearray: the rotated key
        """
        timestamp = current_timestamp & (~(2**rotation_interval_power - 1))

        try:
            from Crypto.Cipher import AES
        except ImportError as error:
            raise NotFoundError(
                "Cryptographic library is not available") from error

        cipher = AES.new(reboot_key, AES.MODE_ECB)
        msg = struct.pack("<LLLL", timestamp, 0, 0, 0)

        return cipher.encrypt(msg)
Exemple #9
0
    def HeaderLength(cls):
        """Return the length of a header needed to calculate this report's length

        Returns:
            int: the length of the needed report header
        """

        raise NotFoundError("IOTileReport HeaderLength needs to be overridden")
    def VerifyRoot(cls, root):
        """Verify that the root key type is known to us.

        Raises:
            NotFoundError: If the key type is not known.
        """

        if root in cls.KnownKeyRoots:
            return

        raise NotFoundError("Unknown key type", key=root)
    def _verify_derive_key(cls, device_id, root, **kwargs):
        report_id = kwargs.get('report_id', None)
        sent_timestamp = kwargs.get('sent_timestamp', None)

        if report_id is None or sent_timestamp is None:
            raise NotFoundError(
                'report_id or sent_timestamp not provided in EnvAuthProvider.sign_report'
            )

        AuthProvider.VerifyRoot(root)

        if root != AuthProvider.UserKey:
            raise NotFoundError('unsupported root key in EnvAuthProvider',
                                root_key=root)

        root_key = cls._get_key(device_id)
        report_key = AuthProvider.DeriveReportKey(root_key, report_id,
                                                  sent_timestamp)

        return report_key
    def get_rotated_key(self, key_type, device_id, **rotation_info):
        """Get a key that is only valid for a limit period of time.

        Args:
            key_type (int): no key, user key or device key
            device_id (int): UUID of the device
            key_info (dict): data that describes the conditions when
                the key is rotated. For example, for a broadcast report key
                it may be the reboot counter of the
                device, the current uptime and the rotation interval of the key.

        Returns:
            bytearray: the rotated key
        """
        counter = rotation_info.get("reboot_counter", None)
        interval_power = rotation_info.get("rotation_interval_power", None)
        timestamp = rotation_info.get("current_timestamp", None)

        if not counter:
            raise NotFoundError(
                'reboot_counter is not provided in EnvAuthProvider get_rotated_key'
            )

        if not interval_power:
            raise NotFoundError(
                'rotation_interval_power is not provided in EnvAuthProvider get_rotated_key'
            )

        if not timestamp:
            raise NotFoundError(
                'timestamp is not provided in EnvAuthProvider get_rotated_key')

        self.verify_key(key_type)

        root_key = self.get_root_key(key_type, device_id)
        reboot_key = self.DeriveRebootKey(root_key, 0, counter)

        temp_key = self.DeriveRotatedKey(reboot_key, timestamp, interval_power)
        return temp_key
Exemple #13
0
    def get_password(cls, device_id):
        """Returns the password from the class

        Args:
            device_id (int): uuid or mac of the device

        Returns:
            bytes: the root key
        """
        if device_id in cls._shared_passwords:
            return cls._shared_passwords[device_id]
        else:
            raise NotFoundError("No key could be found for device",
                                device_id=device_id)
    def check(self, depinfo, deptile, depsettings):
        """Check if this dependency is the latest version in a subclass defined way

        Args:
            depinfo (dict): a dictionary returned by IOTile.dependencies describing the dependency
            deptile (IOTile): an IOTile object for the installed dependency
            depsettings (dict): a dictionary that was previously stored with this dependency by resolve

        Returns:
            bool: True meaning the dependency is up-to-date or False if it is not.
        """

        raise NotFoundError(
            "DependencyResolver did not implement check method")
    def verify_report(self, device_id, root, data, signature, **kwargs):
        """Verify a buffer of report data on behalf of a device.

        Args:
            device_id (int): The id of the device that we should encrypt for
            root (int): The root key type that should be used to generate the report
            data (bytearray): The data that we should verify
            signature (bytearray): The signature attached to data that we should verify
            **kwargs: There are additional specific keyword args that are required
                depending on the root key used.  Typically, you must specify
                - report_id (int): The report id
                - sent_timestamp (int): The sent timestamp of the report

                These two bits of information are used to construct the per report
                signing and encryption key from the specific root key type.

        Returns:
            dict: The result of the verification process must always be a bool under the
                'verified' key, however additional keys may be present depending on the
                signature method used.

        Raises:
            NotFoundError: If the auth provider is not able to verify the data due to
                an error.  If the data is simply not valid, then the function returns
                normally.
        """

        AuthProvider.VerifyRoot(root)

        if root != AuthProvider.NoKey:
            raise NotFoundError('unsupported root key in BasicAuthProvider',
                                root_key=root)

        result = bytearray(hashlib.sha256(data).digest())

        if len(signature) == 0:
            verified = False
        elif len(signature) > len(result):
            verified = False
        elif len(signature) < len(result):
            trunc_result = result[:len(signature)]
            verified = hmac.compare_digest(signature, trunc_result)
        else:
            verified = hmac.compare_digest(signature, result)

        return {'verified': verified, 'bit_length': 8 * len(signature)}
Exemple #16
0
    def get_rotated_key(self, key_type, device_id, **rotation_info):
        """Deligates call to auth providers in the chain

        Args:
            key_type (int): see KnownKeyRoots
            device_id (int): uuid of the device
            rotation_info (dict): specific value for every auth provider

        Returns:
            bytes: the rotated key
        """
        for _priority, provider in self.providers:
            try:
                return provider.get_rotated_key(key_type, device_id,
                                                **rotation_info)
            except NotFoundError:
                pass

        raise NotFoundError(
            "get_rotated_key method is not implemented in any sub_providers")
    def resolve(self, depinfo, destdir):
        """Attempt to resolve this dependency using a subclass defined method
        
        Args:
            depinfo (dict): a dictionary returned by IOTile.dependencies describing the dependency
            destdir (string): the directory that the dependency should be copied into

        Returns:
            dict: The function returns a dictionary that has required and optional keys.  The 
                required keys are:
                    found: boolean if this resolver found a matching dependency

                optional keys are:
                    stop:   boolean if this resolver is suggesting that we should stop looking for this
                            dependency. This is useful for making a resolver that stops a resolver chain
                    info:   a dictionary that contains information that this resolver wants to store
                            with the dependency for future reference.
        """

        raise NotFoundError(
            "DependencyResolver did not implement resolve method")
Exemple #18
0
    def get_root_key(self, key_type, device_id):
        """Deligates call to auth providers in the chain.
        
        This function will attempt to use a device alias if it exists.
        This allows support of using both UUID and device MAC.

        Args:
            key_type (int): see KnownKeyRoots
            device_id (int): ID of the device

        Returns:
            bytes: the root key
        """
        for _priority, provider in self.providers:
            try:
                return provider.get_root_key(key_type, device_id)
            except NotFoundError:
                pass

        raise NotFoundError(
            "get_serialized_key method is not implemented in any sub_providers"
        )
Exemple #19
0
    def ReportLength(cls, header):
        """Given a header of HeaderLength bytes, calculate the size of this report
        """

        raise NotFoundError("IOTileReport ReportLength needs to be overriden")
Exemple #20
0
    def decode(self):
        """Decode a raw report into a series of readings
        """

        raise NotFoundError("IOTileReport decode needs to be overriden")