def test_03_hash(self): import os val = os.urandom(16) seed = os.urandom(16) h1 = hash(val, seed) self.assertEqual(h1, hash(val, seed)) seed2 = os.urandom(16) self.assertNotEqual(h1, hash(val, seed2))
def check_otp(self, anOtpVal, counter=None, window=None, options=None): """ check if the given OTP value is valid for this token. :param anOtpVal: the to be verified otpvalue :type anOtpVal: string :param counter: the counter state, that should be verified :type counter: int :param window: the counter +window, which should be checked :type window: int :param options: the dict, which could contain token specific info :type options: dict :return: the counter state or -1 :rtype: int """ res = -1 tans = self.get_tokeninfo() for tankey, tanvalue in tans.items(): if tankey.startswith("tan.tan"): salt, tan = tanvalue.split(":") if tan == hash(anOtpVal, salt): self.del_tokeninfo(tankey) return 1 return res
def update(self, param, reset_failcount=True): if "tans" in param: # init tokens with tans tans = param.get("tans").split() tan_dict = {k: v for k, v in enumerate(tans)} # Avoid to generate TANs in the superclass PaperToken, since we get the tans from params param["papertoken_count"] = 0 # Determine the otplen from the TANs if len(tans) > 0: param["otplen"] = len(tans[0]) PaperTokenClass.update(self, param, reset_failcount=reset_failcount) else: # Init token without tans, so we create tans in the superclass PaperToken param["papertoken_count"] = param.get( "tantoken_count") or DEFAULT_COUNT PaperTokenClass.update(self, param, reset_failcount=reset_failcount) # After this creation, the init_details contain the complete list of the TANs tan_dict = self.init_details.get("otps", {}) for tankey, tanvalue in tan_dict.items(): # Get a 4 byte salt from the crypto module salt = geturandom(SALT_LENGTH, hex=True) # Now we add all TANs to the tokeninfo of this token. hashed_tan = hash(tanvalue, salt) self.add_tokeninfo("tan.tan{0!s}".format(tankey), "{0}:{1}".format(salt, hashed_tan))
def check_otp(self, anOtpVal, counter=None, window=None, options=None): """ check if the given OTP value is valid for this token. :param anOtpVal: the to be verified otpvalue :type anOtpVal: string :param counter: the counter state, that should be verified :type counter: int :param window: the counter +window, which should be checked :type window: int :param options: the dict, which could contain token specific info :type options: dict :return: the counter state or -1 :rtype: int """ res = -1 tans = self.get_tokeninfo() for tankey, tanvalue in tans.items(): if tankey.startswith("tan.tan"): salt, tan = tanvalue.split(":") if tan == binascii.hexlify(hash(anOtpVal,salt)): self.del_tokeninfo(tankey) return 1 return res
def getHashedPin(self, pin): # TODO: we could log the PIN here. log.debug('getHashedPin()') ## calculate a hash from a pin # Fix for working with MS SQL servers # MS SQL servers sometimes return a '<space>' when the column is empty: '' seed_str = self._fix_spaces(self.privacyIDEASeed) seed = binascii.unhexlify(seed_str) hPin = hash(pin, seed) log.debug("hPin: %s, pin: %s, seed: %s" % (binascii.hexlify(hPin), pin, self.privacyIDEASeed)) return binascii.hexlify(hPin)
def test_05_old_hashes(self): from privacyidea.lib.crypto import hash # Test that old hashes do not break the code r = cleanup(100000000) # Add an entry with an old password hash AuthCache("grandpa", self.realm, self.resolver, hash("old password", seed=""), first_auth=datetime.datetime.utcnow() - datetime.timedelta( minutes=10), last_auth=datetime.datetime.utcnow() - datetime.timedelta( minutes=2)).save() r = verify_in_cache("grandpa", self.realm, self.resolver, "old password") self.assertFalse(r)
def update(self, param, reset_failcount=True): if "tans" in param: # init tokens with tans tans = param.get("tans").split() tan_dict = {k: v for k, v in enumerate(tans)} # Avoid to generate TANs in the superclass PaperToken, since we get the tans from params param["papertoken_count"] = 0 # Determine the otplen from the TANs if len(tans) > 0: param["otplen"] = len(tans[0]) PaperTokenClass.update(self, param, reset_failcount=reset_failcount) else: # Init token without tans, so we create tans in the superclass PaperToken param["papertoken_count"] = param.get("tantoken_count") or DEFAULT_COUNT PaperTokenClass.update(self, param, reset_failcount=reset_failcount) # After this creation, the init_details contain the complete list of the TANs tan_dict = self.init_details.get("otps", {}) for tankey, tanvalue in tan_dict.items(): # Get a 4 byte salt from the crypto module salt = geturandom(SALT_LENGTH, hex=True) # Now we add all TANs to the tokeninfo of this token. hashed_tan = binascii.hexlify(hash(tanvalue, salt)) self.add_tokeninfo("tan.tan{0!s}".format(tankey), "{0}:{1}".format(salt, hashed_tan))
def setHashedPin(self, pin): log.debug('setHashedPin()') seed = geturandom(16) self.privacyIDEASeed = unicode(binascii.hexlify(seed)) self.privacyIDEAPinHash = unicode(binascii.hexlify(hash(pin, seed))) return self.privacyIDEAPinHash
def _hash_password(password): return hash(password, seed="")
def generic_challenge_response_reset_pin(wrapped_function, *args, **kwds): """ Check if the authentication was successful, but if the token needs to reset its PIN. Conditions: To do so we check for "next_pin_change" in the tokeninfo data. This is however easily done using token.is_pin_change(). Policies: A policy defines, if this PIN reset functionality should be active at all. scope=AUTH, action=CHANGE_PIN_VIA_VALIDATE args are: :param tokenobject_list: The list of all the tokens of the user, that will be checked :param passw: The password presented in the authentication. We need this for the PIN reset. kwds are: :param options: options dictionary containing g :param user: The user_obj """ # Before we call the wrapped function, we need to check, if we have a generic challenge # for the given transaction_id and if the token serial matches a given token options = kwds.get("options") or {} user_obj = kwds.get("user") transaction_id = options.get("transaction_id") or options.get("state") if transaction_id: challenges = get_challenges(transaction_id=transaction_id, challenge=CHALLENGE_TYPE.PIN_RESET) if len(challenges) == 1: challenge = challenges[0] # check if challenge matches a token and if it is valid token_obj = next(t for t in args[0] if t.token.serial == challenge.serial) if token_obj: # Then either verify the PIN or set the PIN the first time. The # PIN from the 1st response is stored in challenge.data if challenge.data: hashedpin = challenge.data[SEED_LENGTH + 1:] seed = challenge.data[0:SEED_LENGTH] # Verify the password if hash(args[1], seed) == hashedpin: g = options.get("g") challenge.set_otp_status(True) token_obj.challenge_janitor() # Success, set new PIN and return success token_obj.set_pin(args[1]) pinpol = Match.token(g, scope=SCOPE.ENROLL, action=ACTION.CHANGE_PIN_EVERY, token_obj=token_obj).action_values(unique=True) # Set a new next_pin_change if pinpol: # Set a new next pin change token_obj.set_next_pin_change(diff=list(pinpol)[0]) else: # Obviously the admin removed the policy for changing pins, # so we will not require to change the PIN again token_obj.del_tokeninfo("next_pin_change") return True, {"message": "PIN successfully set.", "serial": token_obj.token.serial} else: return False, {"serial": token_obj.token.serial, "message": "PINs do not match"} else: # The PIN is presented the first time. # Verify if the PIN adheres to the PIN policies. This is always in the normal user context g = options.get("g") g.logged_in_user = {"role": SCOPE.USER} if user_obj: # check_pin below originally works for logged in users, since only logged in users # are allowed to change the pin. So we need to construct a logged_in_user object, otherwise # check_pin would fail. g.logged_in_user["username"] = user_obj.login g.logged_in_user["realm"] = user_obj.realm check_pin(g, args[1], token_obj.token.tokentype, user_obj) # We need to ask for a 2nd time challenge.set_otp_status(True) seed = get_rand_digit_str(SEED_LENGTH) reply_dict = _create_pin_reset_challenge(token_obj, _("Please enter the new PIN again"), "{0!s}:{1!s}".format(seed, hash(args[1], seed))) return False, reply_dict success, reply_dict = wrapped_function(*args, **kwds) # After a successful authentication, we might start the PIN change process if success and reply_dict.get("pin_change"): g = options.get("g") # Determine the realm by the serial serial = reply_dict.get("serial") # The tokenlist can contain more than one token. So we get the matching token object token_obj = next(t for t in args[0] if t.token.serial == serial) if g and Match.token(g, scope=SCOPE.AUTH, action=ACTION.CHANGE_PIN_VIA_VALIDATE, token_obj=token_obj).any(): reply_dict = _create_pin_reset_challenge(token_obj, _("Please enter a new PIN")) return False, reply_dict return success, reply_dict