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
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
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})
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
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)
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()
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 = {}
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()
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()
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)
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)
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)
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)
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)
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()