system: You are an expert security code reviewer. You will be shown a source code file along with a multi-select question listing several CWE (Common Weakness Enumeration) categories. Your task is to identify which of the listed CWEs are present in the code.

OUTPUT FORMAT: Respond with the letter(s) of all correct options, sorted alphabetically and comma-separated, wrapped in a single \boxed{...}. Examples: `\boxed{A}` for one correct, `\boxed{A,C}` for two, `\boxed{A,C,E}` for three. Output the boxed answer ONLY -- no explanation, no other text.

user: ```python
"""Tools used across the project."""
import hashlib
import random
import time
import string
import functools


from oslo_log import log as logging

import redis

from asciipic.common import constant
from asciipic.common import exception
from asciipic import config as asciipic_config


CONFIG = asciipic_config.CONFIG
LOG = logging.getLogger(__name__)

CHARS = string.ascii_uppercase + string.digits


def get_salt(length):
    """Get a cryptographically secure salt."""
    return ''.join(random.SystemRandom().choice(CHARS) for _ in range(length))


def hash_password(password):
    """Return the hash of the password and the salt.:

    :param password: The password you want to hash
    :return: (hash, salt)
    """
    salt = get_salt(constant.SALT_LEN)
    password_template = constant.PASSWORD_HASH_TEMPLATE.format(
        alg=constant.HASHING_ALG,
        password=password,
        salt=salt)
    password_template = password_template.encode()
    hashed_password = hashlib.sha256(password_template).hexdigest()
    return (hashed_password, salt)


def check_password(raw_password, enc_password, salt):
    """Check if the raw password matches the hashed one.:

    :param raw_password: The raw password
    :param enc_password: The encripted/hashed password
    :param salt: The salt used in hasing

    :rtype: True if the password matches
    """
    password_template = constant.PASSWORD_HASH_TEMPLATE.format(
        alg=constant.HASHING_ALG,
        password=raw_password,
        salt=salt)
    password_template = password_template.encode()
    hashed_password = hashlib.sha256(password_template).hexdigest()
    return enc_password == hashed_password


def generate_token(userid):
    """Generate a random token for the user_id"""
    salt = get_salt(128)
    token_template = constant.TOKEN_HASHING_TEMPLATE.format(
        userid=userid, time=time.time(), salt=salt)

    sha512 = hashlib.sha512()
    sha512.update(token_template.encode())
    return sha512.hexdigest()


class RedisConnection(object):

    """High level wrapper over the redis data structures operations."""

    def __init__(self, **kwargs):
        """Instantiates objects able to store and retrieve data.

        :param host: The host for the redis connection
        :param port: The port for the redis connection
        :param db: The redis database
        :param password: Password for the redis connection

        If this parameters are not provided the config values will
        be used.
        """
        self._rcon = None
        self._host = kwargs.get("host", CONFIG.redis.host)
        self._port = kwargs.get("port", CONFIG.redis.port)
        self._db = kwargs.get("db", CONFIG.redis.database)
        self._password = kwargs.get("password", CONFIG.redis.password)
        self._url = kwargs.get("url", CONFIG.redis.url)
        self.refresh()

    def _connect(self):
        """Try establishing a connection until succeeds."""
        try:
            if self._url:
                LOG.info("Using redis URL (%s) to connect.",
                         constant.REDIS_URL_ENV)
                rcon = redis.StrictRedis.from_url(self._url)
            else:
                LOG.info("Using separate config options to connect"
                         " to redis.")
                rcon = redis.StrictRedis(host=self._host, port=self._port,
                                         db=self._db,
                                         password=self._password)
            # Return the connection only if is valid and reachable
            if not rcon.ping():
                return None
        except (redis.ConnectionError, redis.RedisError) as exc:
            LOG.error("Failed to connect to Redis Server: %s", exc)
            return None

        return rcon

    def refresh(self, tries=3):
        """Re-establish the connection only if is dropped."""
        for _ in range(tries):
            try:
                if not self._rcon or not self._rcon.ping():
                    self._rcon = self._connect()
                else:
                    break
            except redis.ConnectionError as exc:
                LOG.error("Failed to connect to Redis Server: %s", exc)
        else:
            raise exception.AsciipicException(
                "Failed to connect to Redis Server.")

        return True

    @property
    def rcon(self):
        """Return a Redis connection."""
        self.refresh()
        return self._rcon


# pylint: disable=invalid-name
class retry(object):
    """Decorator that allow the method to be fail."""
    def __init__(self, retry_count=constant.RETRY_COUNT,
                 retry_delay=constant.RETRY_DELAY,
                 exceptions=(Exception, )):
        """Initializa the parameters.

        :param retry_count: Now many times to try
        :param retry_delay: Delay between tries
        :param exception: The exception that sould be catched
                          can be one exception or a tuple of exceptions
        """
        self._retry_count = retry_count
        self._retry_delay = retry_delay
        if isinstance(exceptions, tuple):
            self._exceptions = exceptions
        else:
            self._exceptions = (exceptions, )

    def __call__(self, method):
        """Wrap the method.

        :param method: The method we want ot wrap.
        """
        @functools.wraps(method)
        def wrapper(*args, **kwargs):
            result = None
            for index in range(self._retry_count):
                try:
                    result = method(*args, **kwargs)
                    break
                # pylint: disable=catching-non-exception
                except self._exceptions as ex:
                    LOG.warn("Fail - %s in <%s.%s> with %s",
                             index, method.__module__,
                             method.__name__, ex)
                    time.sleep(self._retry_delay)
            else:
                # Too many tries
                raise exception.FailedTooManyTimes(method=method.__name__,
                                                   module=method.__module__)
            return result
        return wrapper

```

Which of the following CWE vulnerability categories are present in this code? Multiple options may be correct.

  A) CWE-611: Improper Restriction of XML External Entity Reference
  B) CWE-312: Cleartext Storage of Sensitive Information
  C) CWE-776: Improper Restriction of Recursive Entity References in DTDs ('XML Entity Expansion')
  D) CWE-601: URL Redirection to Untrusted Site ('Open Redirect')
  E) CWE-327: Use of a Broken or Risky Cryptographic Algorithm
  F) CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Answer: E