Ejemplo n.º 1
0
    def __init__(self, config=None):
        """
        Initialize the PKCS11 Security Module.
        The configuration needs to contain the pkcs11 module and the ID of the key.

          {"module": "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so",
           "key_id": 10}

        The HSM is not directly ready, since the HSM is protected by a password.
        The function setup_module({"password": "******"}) needs to be called.

        :param config: contains the HSM configuration
        :type config: dict

        :return: The Security Module object
        """
        config = config or {}
        self.name = "PKCS11"
        self.config = config
        self.is_ready = False

        if "module" not in config:
            log.error("No PKCS11 module defined!")
            raise HSMException("No PKCS11 module defined.")
        self.key_id = config.get("key_id", 16)
        self.pkcs11 = PyKCS11.PyKCS11Lib()
        self.pkcs11.load(config.get("module"))
        self.session = None
        self.key_handle = None
Ejemplo n.º 2
0
    def setup_module(self, params):
        """
        callback, which is called during the runtime to initialze the
        security module.

        Here the password for the PKCS11 HSM can be provided

           {"password": "******"}

        :param params: The password for the HSM
        :type  params: dict

        :return: -
        """
        if "password" in params:
            PASSWORD = str(params.get("password"))
        else:
            raise HSMException("missing password")

        slotlist = self.pkcs11.getSlotList()
        if not len(slotlist):
            raise HSMException("No HSM connected")
        slot_id = slotlist[0]
        # If the HSM is not connected at this point, it will fail
        self.session = self.pkcs11.openSession(slot=slot_id)

        slotinfo = self.pkcs11.getSlotInfo(1)
        log.info("Setting up {}".format(
            slotinfo.fields.get("slotDescription")))
        # get the public key
        objs = self.session.findObjects([(PyKCS11.CKA_CLASS,
                                          PyKCS11.CKO_PUBLIC_KEY),
                                         (PyKCS11.CKA_ID, (self.key_id, ))])
        self.key_handle = objs[0]
        pubkey_list = self.session.getAttributeValue(self.key_handle,
                                                     [PyKCS11.CKA_VALUE],
                                                     allAsBinary=True)
        pubkey_bin = int_list_to_bytestring(pubkey_list[0])
        self.rsa_pubkey = RSA.importKey(pubkey_bin)

        # We do a login and logout to see if
        self.session.login(PASSWORD)
        self.session.logout()
        self.is_ready = True
        log.info("Successfully setup the security module.")

        return self.is_ready
Ejemplo n.º 3
0
def set_hsm_password(password):
    """
    Set the password for the HSM. Raises an exception if the HSM is already set up.
    :param password: password string
    :return: boolean flag indicating whether the HSM is ready now
    """
    hsm = init_hsm()
    if hsm.is_ready:
        raise HSMException("HSM already set up.")
    return hsm.setup_module({"password": password})
Ejemplo n.º 4
0
    def _login(self):
        slotlist = self.pkcs11.getSlotList()
        log.debug("Found the slots: {0!s}".format(slotlist))
        if not len(slotlist):
            raise HSMException("No HSM connected. No slots found.")
        if self.slot == -1 and len(slotlist) == 1:
            # Use the first and only slot
            self.slot = slotlist[0]
        if self.slot not in slotlist:
            raise HSMException("Slot {0:d} not present".format(self.slot))

        slotinfo = self.pkcs11.getSlotInfo(self.slot)
        log.debug("Setting up '{}'".format(slotinfo.slotDescription))

        # Before starting the session, we log the old session time usage
        log.debug(
            "Starting new session now. The old session started {0!s} seconds ago."
            .format(datetime.datetime.now() - self.session_start_time))
        log.debug(
            "Starting new session now. The old session was used {0!s} seconds ago."
            .format(datetime.datetime.now() - self.session_lastused_time))
        # If the HSM is not connected at this point, it will fail
        self.session = self.pkcs11.openSession(slot=self.slot)
        self.session_start_time = datetime.datetime.now()

        log.debug("Logging on to '{}'".format(slotinfo.slotDescription))
        self.session.login(self.password)

        for k in self.mapping:
            label = self.key_labels[k]
            objs = self.session.findObjects([(PyKCS11.CKA_CLASS,
                                              PyKCS11.CKO_SECRET_KEY),
                                             (PyKCS11.CKA_LABEL, label)])
            log.debug("Loading '{}' key with label '{}'".format(k, label))
            if objs:
                self.key_handles[self.mapping[k]] = objs[0]

        # self.session.logout()
        log.debug("Successfully setup the security module.")
        self.is_ready = True
Ejemplo n.º 5
0
def set_security_module():
    """
    Set the password for the security module
    """
    password = getParam(request.all_data, "password", required)
    HSM = current_app.config["pi_hsm"]
    hsm = HSM.get("obj")
    if hsm.is_ready:
        raise HSMException("HSM already set up.")

    is_ready = hsm.setup_module({"password": password})
    res = {"is_ready": is_ready}
    g.audit_object.log({'success': res})
    return send_result(res)
Ejemplo n.º 6
0
    def __init__(self, config=None):
        """
        Initialize the PKCS11 Security Module.
        The configuration needs to contain the pkcs11 module and the ID of the key.

        {"module": "/usr/lib/hsm_pkcs11.so", "slot": 42, "key_label": "privacyidea"}

        The HSM is not directly ready, since the HSM is protected by a password.
        The function setup_module({"password": "******"}) needs to be called.

        :param config: contains the HSM configuration
        :type config: dict

        :return: The Security Module object
        """
        config = config or {}
        self.name = "HSM"
        self.config = config
        # Initially, we might be missing a password
        self.is_ready = False

        if "module" not in config:
            log.error("No PKCS11 module defined!")
            raise HSMException("No PKCS11 module defined.")

        label_prefix = config.get("key_label", "privacyidea")
        self.key_labels = {}
        for k in self.mapping:
            l = config.get(("key_label_{0!s}".format(k)))
            l = ('{0!s}_{1!s}'.format(label_prefix, k)) if l is None else l
            self.key_labels[k] = l

        log.debug("Setting key labels: {0!s}".format(self.key_labels))
        # convert the slot to int
        self.slot = int(config.get("slot", 1))
        log.debug("Setting slot: {0!s}".format(self.slot))
        self.password = config.get("password")
        log.debug("Setting a password: {0!s}".format(bool(self.password)))
        self.module = config.get("module")
        log.debug("Setting the modules: {0!s}".format(self.module))
        self.max_retries = config.get("max_retries", MAX_RETRIES)
        log.debug("Setting max retries: {0!s}".format(self.max_retries))
        self.session = None
        self.session_start_time = datetime.datetime.now()
        self.session_lastused_time = datetime.datetime.now()
        self.key_handles = {}

        self.initialize_hsm()
Ejemplo n.º 7
0
    def __init__(self, config=None):
        """
        Init of the default security module. The config needs to contain the key
        file. THe key file can be encrypted, than the config also needs to
        provide the information, that the key file is encrypted.

           {"file": "/etc/secretkey",
            "crypted": True}

        If the key file is encrypted, the HSM is not immediatly ready. It will
        return HSM.is_ready == False.
        Then the function "setup_module({"password": "******"}) needs
        to be called.

        :param config: contains the configuration definition
        :type  config: dict

        :return -
        """
        config = config or {}
        self.name = "Default"
        self.config = config
        self.crypted = False
        self.is_ready = True
        self._id = binascii.hexlify(os.urandom(3))

        if "file" not in config:
            log.error("No secret file defined. A parameter "
                      "PI_ENCFILE is missing in your pi.cfg.")
            raise HSMException("no secret file defined: PI_ENCFILE!")

        # We determine, if the file is encrypted.
        with open(config.get("file")) as f:
            cipher = f.read()

        if len(cipher) > 100:
            config["crypted"] = True

        if "crypted" in config:
            if config.get("crypted") is True or config.get('crypted').lower() \
                    == "true":
                self.crypted = True
                self.is_ready = False

        self.secFile = config.get('file')
        self.secrets = {}
Ejemplo n.º 8
0
    def setup_module(self, params):
        """
        callback, which is called during the runtime to initialze the
        security module.

        Here the password for the PKCS11 HSM can be provided

           {"password": "******"}

        :param params: The password for the HSM
        :type  params: dict

        :return: -
        """
        if "password" in params:
            self.password = str(params.get("password"))
        else:
            raise HSMException("missing password")
        return self._login()
Ejemplo n.º 9
0
    def __init__(self, config=None):
        """
        Initialize the PKCS11 Security Module.
        The configuration needs to contain the pkcs11 module and the ID of the key.

        {"module": "/usr/lib/hsm_pkcs11.so", "slot": 42, "key_label": "privacyidea"}

        The HSM is not directly ready, since the HSM is protected by a password.
        The function setup_module({"password": "******"}) needs to be called.

        :param config: contains the HSM configuration
        :type config: dict

        :return: The Security Module object
        """
        config = config or {}
        self.name = "HSM"
        self.config = config
        self.is_ready = False

        if "module" not in config:
            log.error("No PKCS11 module defined!")
            raise HSMException("No PKCS11 module defined.")

        label_prefix = config.get("key_label", "privacyidea")
        self.key_labels = {}
        for k in ['token', 'config', 'value']:
            l = config.get(("key_label_{0!s}".format(k)))
            l = ('{0!s}_{0!s}'.format(label_prefix, k)) if l is None else l
            self.key_labels[k] = l

        self.slot = config.get("slot", 1)
        self.password = config.get("password")
        self.module = config.get("module")
        self.session = None
        self.key_handles = {}

        self.pkcs11 = PyKCS11.PyKCS11Lib()
        self.pkcs11.load(self.module)
        self.pkcs11.lib.C_Initialize()

        if self.password:
            self._login()
Ejemplo n.º 10
0
    def decrypt(self, data, iv, key_id=TOKEN_KEY):
        if len(data) == 0:
            return bytes("")
        log.debug("Decrypting {} bytes with key {}".format(len(data), key_id))
        m = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
        k = self.key_handles[key_id]
        retries = 0
        while True:
            try:
                r = self.session.decrypt(k, bytes(data), m)
                break
            except PyKCS11.PyKCS11Error as exx:
                log.warning(u"Decryption failed: {0!s}".format(exx))
                self.initialize_hsm()
                retries += 1
                if retries > MAX_RETRIES:
                    raise HSMException("Failed to decrypt after multiple retries.")

        return int_list_to_bytestring(r)
Ejemplo n.º 11
0
    def random(self, length):
        """
        Return a random bytestring
        :param length: length of the random bytestring
        :rtype bytes
        """
        retries = 0
        while True:
            try:
                r_integers = self.session.generateRandom(length)
                break
            except PyKCS11.PyKCS11Error as exx:
                log.warning(u"Generate Random failed: {0!s}".format(exx))
                # If something goes wrong in this process, we free memory, session and handles
                self.pkcs11.lib.C_Finalize()
                self.initialize_hsm()
                retries += 1
                if retries > self.max_retries:
                    raise HSMException("Failed to generate random number after multiple retries.")

        # convert the array of the random integers to a string
        return int_list_to_bytestring(r_integers)
Ejemplo n.º 12
0
    def random(self, length):
        """
        Return a random bytestring
        :param length: length of the random bytestring
        :return:
        """
        retries = 0
        while True:
            try:
                r_integers = self.session.generateRandom(length)
                break
            except PyKCS11.PyKCS11Error as exx:
                log.warning(u"Generate Random failed: {0!s}".format(exx))
                self.initialize_hsm()
                retries += 1
                if retries > MAX_RETRIES:
                    raise HSMException(
                        "Failed to generate random number after multiple retries."
                    )

        # convert the array of the random integers to a string
        return int_list_to_bytestring(r_integers)
Ejemplo n.º 13
0
    def decrypt(self, data, iv, key_id=TOKEN_KEY):
        if len(data) == 0:
            return bytes("")
        log.debug("Decrypting {} bytes with key {}".format(len(data), key_id))
        m = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
        retries = 0
        while True:
            try:
                k = self.key_handles[key_id]
                r = self.session.decrypt(k, bytes(data), m)
                break
            except PyKCS11.PyKCS11Error as exx:
                log.warning(u"Decryption failed: {0!s}".format(exx))
                # If something goes wrong in this process, we free memory, session and handlers
                self.pkcs11.lib.C_Finalize()
                self.initialize_hsm()
                retries += 1
                if retries > self.max_retries:
                    raise HSMException(
                        "Failed to decrypt after multiple retries.")

        return int_list_to_bytestring(r)
Ejemplo n.º 14
0
    def decrypt(self, enc_data, iv, key_id=SecurityModule.TOKEN_KEY):
        """

        :rtype bytes
        """
        if len(enc_data) == 0:
            return bytes("")
        log.debug("Decrypting {} bytes with key {}".format(
            len(enc_data), key_id))
        m = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
        start = datetime.datetime.now()
        retries = 0
        while True:
            try:
                k = self.key_handles[key_id]
                r = self.session.decrypt(k, bytes(enc_data), m)
                self.session_lastused_time = datetime.datetime.now()
                break
            except PyKCS11.PyKCS11Error as exx:
                log.warning(u"Decryption retry: {0!s}".format(exx))
                # If something goes wrong in this process, we free memory, session and handlers
                self.pkcs11.lib.C_Finalize()
                self.initialize_hsm()
                retries += 1
                if retries > self.max_retries:
                    td = datetime.datetime.now() - start
                    log.warning(
                        u"Decryption finally failed: {0!s}. Time taken: {1!s}."
                        .format(exx, td))
                    raise HSMException(
                        "Failed to decrypt after multiple retries.")

        if retries > 0:
            td = datetime.datetime.now() - start
            log.warning(
                u"Decryption after {0!s} retries successful. Time taken: {1!s}."
                .format(retries, td))
        return int_list_to_bytestring(r)
Ejemplo n.º 15
0
    def __init__(self, config=None, logout=True):
        """
        Initialize the PKCS11 Security Module.
        The configuration needs to contain the pkcs11 module and the ID of the key.

        {"module": "/usr/lib/libykcs11.so",
         "slotname": "Yubico YubiKey",
         "keyid": 1,
         "keylabel": "my secret key"
         "encfile": "/etc/privacyidea/enckey.enc",
         "password", "123456"}

        The encfile is the encrypted encryption key.

        The values can be configured in the pi.cfg file like this:

        PI_HSM_MODULE = "privacyidea.lib.security.encryptkey.EncryptKeyHardwareSecurityModule"
        PI_HSM_MODULE_MODULE = "/usr/lib/libykcs11.so"
        PI_HSM_MODULE_SLOTNAME = "Yubico YubiKey"
        # Alternative to slotname:
        # PI_HSM_MODULE_SLOT = 1
        PI_HSM_MODULE_KEYID = 1
        # Alternative to KEYID -- only use one of both!
        PI_HSM_MODULE_KEYLABEL = "my secret key"
        PI_HSM_MODULE_PASSWORD = '******'
        PI_HSM_MODULE_ENCFILE = "/etc/privacyidea/enckey.enc"

        :param config: contains the HSM configuration
        :type config: dict

        :return: The Security Module object
        """
        self.config = config or {}
        self.secrets = {}
        self.name = "HSM"
        self.slot = self.config.get("slot") or -1
        self.slotname = self.config.get("slotname")
        if "module" not in config:
            log.error("No PKCS11 module defined!")
            raise HSMException("No PKCS11 module defined.")
        else:
            self.module = self.config.get("module")

        if "keyid" not in config and "keylabel" not in config:
            log.error("No keyid or keylabel defined.")
            raise HSMException("No keyid or keylabel defined.")
        self.keyid = self.config.get("keyid")
        self.keylabel = self.config.get("keylabel")

        if "password" in self.config:
            self.password = self.config.get("password")
        else:
            log.error("No password specified.")
            self.password = getpass()
        # Now we have our password
        self.is_ready = False

        timeout = self.config.get("timeout") or DEFAULT_TIMEOUT

        with hsm_lock(timeout=timeout):
            self.pkcs11 = PyKCS11.PyKCS11Lib()
            self.pkcs11.load(self.config.get("module"))
            self.pkcs11.lib.C_Initialize()

            slotlist = self.pkcs11.getSlotList()
            log.debug("Found the slots: {0!s}".format(slotlist))
            if not len(slotlist):
                raise HSMException("No HSM connected. No slots found.")

            if self.slot == -1:
                if len(slotlist) == 1:
                    # Use the first and only slot
                    self.slot = slotlist[0]
                elif len(slotlist) > 1:
                    for slot in slotlist:
                        # Find the slot via the slotname
                        slotinfo = self.pkcs11.getSlotInfo(slot)
                        log.debug("Found slot '{}'".format(
                            slotinfo.slotDescription))
                        if slotinfo.slotDescription.startswith(self.slotname):
                            self.slot = slot
                            break

            if self.slot not in slotlist:
                raise HSMException("Slot {0:d} ({1:s}) not present".format(
                    self.slot, self.slotname))

            slotinfo = self.pkcs11.getSlotInfo(self.slot)
            log.debug("Setting up slot {0!s}: '{1!s}'".format(
                self.slot, slotinfo.slotDescription))

            self.session = self.pkcs11.openSession(slot=self.slot)
            log.debug("Logging on to '{}'".format(slotinfo.slotDescription))
            try:
                self.session.login(self.password)
            except PyKCS11.PyKCS11Error as e:
                if str(e).startswith("CKR_USER_ALREADY_LOGGED_IN"):
                    log.info("Timing issues. We need to relogin the user.")
                    # in case the user is already logged in
                    self.session.logout()
                    self.session.login(self.password)
                elif str(e).startswith("CKR_PIN_INCORRECT"):
                    log.error(
                        "A wrong HSM Password is configured. Please check your configuration in pi.cfg"
                    )
                    # We reset the password, to avoid future PIN Locking!
                    # I think this does not work between processes!
                    self.password = None
                    raise e
                else:
                    raise e

            if "encfile" in self.config:
                self._decrypt_file(self.config.get("encfile"))
            log.debug("Successfully setup the security module.")
            self.is_ready = True
            # We need this for the base class
            self.crypted = True
            if logout:
                self.session.logout()
                self.session.closeSession()