Exemplo n.º 1
0
def generate_keys():
    """
    Generates the public and private keys necessary for encryption and signing.
    """
    sodium_client = SodiumClient()

    [sign_pk, sign_sk] = sodium_client.sign_generate_keypair()
    [box_pk, box_sk] = sodium_client.box_generate_keypair()

    return EncryptionKeys(sign_pk, sign_sk, box_pk, box_sk)
Exemplo n.º 2
0
class SplunkEncryptionContext(EncryptionContext):
    """
    Context object for handling generating and fetching public and private keys. (Currently only public key is
    supported)
    """

    def __init__(self, session_key, app_name, sodium_client=None):
        """
        Pass a session token and create a KV Store Handler to be able to write and fetch public keys from KV Store.
        The session token itself is not exposed, only the handler.
        """

        self.mode = SdkMode.SPLUNK
        if sodium_client:
            self.sodium_client = sodium_client
        else:
            self.sodium_client = SodiumClient()

        self._key_cache = {}
        self.session_key = session_key
        self.app_name = app_name

        self.generate_keys()

    def set_encryption_keys(self, keys_dict):
        self._key_cache = keys_dict

    def _cache_keys(self):
        result = False
        raw_data = splunk_client.fetch_sensitive_data(self.session_key, ENCRYPTION_KEYS, self.app_name)
        try:
            self._key_cache = EncryptionKeys.from_json(json.loads(raw_data)).__dict__
        except Exception:
            self._key_cache = {}

        if SIGN_PUBLIC_KEY in self._key_cache:
            result = True

        return result

    def _create_key_bucket(self):
        try:
            splunk_client.create_sensitive_data(self.session_key, ENCRYPTION_KEYS, '{}', self.app_name)
        except RESTException as e:
            if e.statusCode != 409:
                raise e

    def generate_keys(self):
        """
        Stores the required signing and encryption keys to KV Store in the meta collection. If the
        public key already exists, then this is a no op.

        There is a synchronization issue where when you call generate keys, KV Store might not yet have been
        initialized. The way we handle this is if we get a 503 HTTP error back from KV Store, this means the store
        is not yet available, in which case we callback generate keys to run in 5 seconds.
        """

        self._create_key_bucket()

        keys_cached = self._cache_keys()

        if not keys_cached:
            [sign_pk, sign_sk] = [k for k in self.sodium_client.sign_generate_keypair()]
            [box_pk, box_sk] = [k for k in self.sodium_client.box_generate_keypair()]

            encryption_keys = EncryptionKeys(sign_pk, sign_sk, box_pk, box_sk)

            key_data = json.dumps(encryption_keys.to_json())
            splunk_client.update_sensitive_data(self.session_key, ENCRYPTION_KEYS, key_data, self.app_name)
            self._key_cache = encryption_keys.__dict__
class DeploymentBundle(BaseRestHandler, PersistentServerConnectionApplication):
    """
    Main class for handling the deployment bundle endpoint. Subclasses the spacebridge_app
    BaseRestHandler.

    """
    def __init__(self, command_line, command_arg):
        BaseRestHandler.__init__(self)
        self.sodium_client = SodiumClient()

    def get(self, request):
        """
        Handler which returns mdm signing public key
        """

        response = {}
        try:
            kvstore_service = kvstore(collection=USER_META_COLLECTION_NAME,
                                      session_key=request[SESSION][AUTHTOKEN],
                                      owner=request[SESSION][USER])
            result = json.loads(
                kvstore_service.get_item_by_key(MDM_KEYPAIR_GENERATION_TIME)
                [1])
            response.update({TIMESTAMP: result[TIMESTAMP]})

        except Exception as e:
            # If key not in kvstore
            if hasattr(e,
                       'statusCode') and e.statusCode == HTTPStatus.NOT_FOUND:
                return {
                    'payload': {
                        'message':
                        'Could not find mdm keypair update time in kvstore',
                        'status': HTTPStatus.NOT_FOUND
                    }
                }
            return {
                'payload': {
                    'message': str(e),
                    'status': HTTPStatus.BAD_REQUEST
                }
            }

        try:
            public_key = fetch_sensitive_data(request[SESSION][AUTHTOKEN],
                                              MDM_SIGN_PUBLIC_KEY)
            private_key = fetch_sensitive_data(request[SESSION][AUTHTOKEN],
                                               MDM_SIGN_PRIVATE_KEY)
            response.update({
                'sign_public_key': public_key,
                'sign_private_key': private_key
            })
        except Exception as e:
            # If key not in storage/passwords
            if hasattr(e,
                       'statusCode') and e.statusCode == HTTPStatus.NOT_FOUND:
                return {
                    'payload': {
                        'message':
                        'Could not find one or both of key={} and key={} in /storage/passwords'
                        .format(MDM_SIGN_PUBLIC_KEY, MDM_SIGN_PRIVATE_KEY),
                        'status':
                        HTTPStatus.NOT_FOUND
                    }
                }

            return {
                'payload': {
                    'message': str(e),
                    'status': HTTPStatus.BAD_REQUEST
                }
            }

        return {'payload': response, 'status': HTTPStatus.OK}

    def post(self, request):
        """
        Handler which generates and returns an mdm keypair
        """

        # generate mdm credentials
        LOGGER.info("Generating MDM Credentials")
        system_authtoken = request[SYSTEM_AUTHTOKEN]
        key_bundle = _load_key_bundle(system_authtoken)

        [public_key, private_key] = self.sodium_client.sign_generate_keypair()
        now = int(datetime.now().strftime('%s'))

        response = {}
        response['message'] = []
        status = HTTPStatus.OK

        try:
            # send public signing key to spacebridge
            send_mdm_signing_key_to_spacebridge(request[SESSION][AUTHTOKEN],
                                                public_key, key_bundle)

        except Exception as e:
            status = HTTPStatus.INTERNAL_SERVER_ERROR
            LOGGER.warn(
                "Failed to register mdm keys with spacebridge. error=%s", e)
            return {
                'payload': {
                    'failed_save': True,
                    'message': e
                },
                'status': status,
            }

        # update key generation timestamp
        try:
            kvstore_service = kvstore(collection=USER_META_COLLECTION_NAME,
                                      session_key=request[SESSION][AUTHTOKEN],
                                      owner=request[SESSION][USER])
            entry = {KEY: MDM_KEYPAIR_GENERATION_TIME, TIMESTAMP: now}
            kvstore_service.insert_or_update_item_containing_key(entry)

        except Exception as e:
            status = HTTPStatus.INTERNAL_SERVER_ERROR
            response['failed_timesave'] = True
            response['message'].append(e.message)

        # store to storage/passwords
        try:
            [_, created_public_key] = update_or_create_sensitive_data(
                request[SESSION][AUTHTOKEN], MDM_SIGN_PUBLIC_KEY,
                py23.b64encode_to_str(public_key))

        except Exception as e:
            status = HTTPStatus.INTERNAL_SERVER_ERROR
            response['failed_public_localsave'] = True
            response['message'].append(str(e))

        try:
            [_, created_private_key] = update_or_create_sensitive_data(
                request[SESSION][AUTHTOKEN], MDM_SIGN_PRIVATE_KEY,
                py23.b64encode_to_str(private_key))

        except Exception as e:
            status = HTTPStatus.INTERNAL_SERVER_ERROR
            response['failed_private_localsave'] = True
            response['message'].append(str(e))

        # don't pass back the message if we have no errors
        if not response['message']:
            del response['message']

        response[SIGN_PUBLIC_KEY] = py23.b64encode_to_str(public_key)
        response[SIGN_PRIVATE_KEY] = py23.b64encode_to_str(private_key)
        response[TIMESTAMP] = now

        return {
            'payload': response,
            'status': status,
        }