Exemple #1
0
 def test_performance_scalability(self):
     """
     Theory: If you run with 100 iterations, it should take 100
     times as long as running with 1 iteration.
     """
     n1, n2 = 100, 10000
     elapsed = lambda f: timeit.timeit(f, number=1)
     t1 = elapsed(lambda: pbkdf2("password", "salt", iterations=n1))
     t2 = elapsed(lambda: pbkdf2("password", "salt", iterations=n2))
     measured_scale_exponent = math.log(t2 / t1, n2 / n1)
     self.assertLess(measured_scale_exponent, 1.1)
Exemple #2
0
    def test_locked_hashes(self):

        # Forcefully lock the context and flush everything related to
        # accounts
        self.hasher.update(secret=None,
                           thresholdlesskey=None,
                           is_unlocked=False)
        cache.clear()

        password1 = make_share('password1')  # get an account

        algorithm, sharenumber, iterations, salt, passhash = \
                password1.split('$',4)
        passhash = passhash.encode('ascii').strip()

        iterations = int(iterations)

        self.assertTrue('pph' == algorithm)
        self.assertTrue(sharenumber.startswith('-'))

        # now let's do a plain pbkdf2 hash and compare the results
        proposed_hash = pbkdf2('password1', salt, iterations, digest=sha256)
        proposed_hash = b64encode(proposed_hash)

        self.assertTrue(proposed_hash == passhash)
Exemple #3
0
 def encode(self, password, salt, iterations=None):
     assert password is not None
     assert salt and '$' not in salt
     iterations = iterations or self.iterations
     hash = pbkdf2(password, salt, iterations, digest=self.digest)
     hash = base64.b64encode(hash).decode('ascii').strip()
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
Exemple #4
0
def change_pw(request):
    if request.method == 'POST' and request.user.is_authenticated:
        if 'cipherKey' not in request.session:
            return redirect('verify_pw')
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)
            data = Passwords.objects.filter(user=request.user)
            _, _, salt, _ = user.password.split('$')
            pw = form.cleaned_data['new_password1']
            key = pbkdf2(pw, salt, 50000, 48)
            key, iv = key[16:], key[0:16]
            for obj in data:
                encryption_suite = AES.new(
                    bytes.fromhex(request.session.get('cipherKey')),
                    AES.MODE_CFB, bytes.fromhex(request.session.get('iv')))
                obj.pw = encryption_suite.decrypt(bytes.fromhex(
                    obj.pw)).decode('utf-8')
                new_encryption_suite = AES.new(key, AES.MODE_CFB, iv)
                obj.pw = new_encryption_suite.encrypt(
                    obj.pw.encode('utf-8')).hex()
                obj.save()
            logout(request)
            return redirect('login')
        else:
            return render(request, 'password/change_pw.html',
                          {'password_change_form': form})

    else:
        form = PasswordChangeForm(request.user)
        return render(request, 'password/change_pw.html',
                      {'password_change_form': form})
Exemple #5
0
    def test_update_hash_threshold(self):

        # we will remove the $ because we are going to create the entry
        # artificially
        salt = get_random_string(6).strip('$')

        password = '******'
        iterations = 12000

        pbkdf2_encoded_password = pbkdf2(password,
                                         salt,
                                         iterations,
                                         digest=sha256)

        pbkdf2_encoded_password = b64encode(pbkdf2_encoded_password)

        polyhash, sharenumber = self.hasher.update_hash_threshold(
            pbkdf2_encoded_password)

        password_string = "{0}${1}${2}${3}${4}".format('pph', sharenumber,
                                                       iterations, salt,
                                                       polyhash)

        # TODO we should cehck whether they can provide partial verification
        self.assertTrue(check(password, password_string))
    def post(self, request):
        user_id = str(self.request.data.get('user_id', '')).strip()

        if not user_id:
            return Response({'error': 'Missing user_id'})

        try:
            login_user = User.objects.filter(id=user_id).get()
        except User.DoesNotExist:
            return Response({'error': 'Unknown user_id'})

        # Remove existing tokens on user
        RatatoskrAdminLoginToken.objects.filter(login_user_id=user_id).delete()

        # Create a new token
        token_str = RatatoskrAdminLoginToken.generate_token()
        salt = crypto.get_random_string()
        iterations = randint(10000, 10100)
        encrypted_token = crypto.pbkdf2(token_str, salt, iterations)

        RatatoskrAdminLoginToken.objects.create(
            token=encrypted_token,
            salt=salt,
            iterations=iterations,
            login_user=login_user,
            admin_user=request.user,
        )

        return Response({'token': token_str})
Exemple #7
0
def get_revocation_key(user):
    """
    When the value returned by this method changes, this revocates tokens.

    It is derived from the hashed password so that changing the password
    revokes tokens.

    For one-time tokens, it also contains the last login datetime so that
    logging in revokes existing tokens.

    """
    data = ""
    if settings.INVALIDATE_ON_PASSWORD_CHANGE:
        data += user.password
    if settings.ONE_TIME:
        data += str(user.last_login)
    # The password is expected to be a secure hash but we hash it again
    # for additional safety. We default to MD5 to minimize the length of
    # the token. (Remember, if an attacker obtains the URL, he can already
    # log in. This isn't high security.)
    return crypto.pbkdf2(
        data,
        settings.SALT,
        settings.ITERATIONS,
        digest=settings.DIGEST,
    )
Exemple #8
0
    def post(self, request):
        phone_number = str(self.request.data.get('phone_number', '')).strip()
        users = lookup_users_by_phone(phone_number)
        users += self.lookup_local_users_by_phone(phone_number)
        if not len(users):
            return Response({'error': 'not found'}, status=403)

        SMSAuthCode.objects.filter(phone_number=phone_number).delete()

        auth_code = SMSAuthCode.generate_code()
        salt = crypto.get_random_string()
        iterations = randint(10000, 10100)
        encrypted_code = crypto.pbkdf2(auth_code, salt, iterations)

        code = SMSAuthCode.objects.create(
            phone_number=phone_number,
            code=encrypted_code,
            salt=salt,
            iterations=iterations
        )
        for user in users:
            code.users.add(user)

        try:
            send_sms(phone_number, 'Din innloggingskode er: %s' % auth_code)
        except SMSGatewayDeliveryError:
            return Response({'error': 'unable to send sms'}, 503)
        except Exception:
            return Response({'error': 'server error'}, 503)

        return Response({'status': 'ok'})
    def save(self):

        invitation_code = self.cleaned_data['invitation_code']
        # make the invitation a invalid/spent.
        invite = UserRegisterCode.objects.get(
            code=str(invitation_code), used=False)

        new_user = User.objects.create_user(
            username=self.cleaned_data['username'],
            first_name=self.cleaned_data['first_name'],
            last_name=self.cleaned_data['last_name'],
            password=self.cleaned_data['password1'],
            email=self.cleaned_data['email'],
            is_active=False)

        UserProfile.objects.create(user=new_user,
                                   user_type="BEN",
                                   create_applications=False,
                                   )

        # TODO: Add Crosswalk Create.
        Crosswalk.objects.create(user=new_user,
                                 user_id_hash=binascii.hexlify(pbkdf2(self.cleaned_data['id_number'],
                                                                      settings.USER_ID_SALT,
                                                                      settings.USER_ID_ITERATIONS)).decode("ascii"))
        #
        group = Group.objects.get(name='BlueButton')
        new_user.groups.add(group)

        # Send a verification email
        create_activation_key(new_user)

        invite.used = True
        invite.save()
        return new_user
def encrypt_match_report(apps, schema_editor):
    db_alias = schema_editor.connection.alias
    MatchReport = apps.get_model('delivery', 'MatchReport')
    for match_report in MatchReport.objects.using(db_alias).all():
        match_report_content = MatchReportContent(
            identifier=match_report.identifier,
            perp_name=match_report.name,
            email=match_report.contact_email,
            phone=match_report.contact_phone,
            contact_name=match_report.contact_name,
            voicemail=match_report.contact_voicemail,
            notes=match_report.contact_notes)
        match_report.salt = get_random_string()
        stretched_identifier = pbkdf2(
            match_report.identifier,
            match_report.salt,
            settings.ORIGINAL_KEY_ITERATIONS,
            digest=hashlib.sha256)
        encrypted_match_report = security.pepper(
            security.encrypt_report(
                stretched_key=stretched_identifier,
                report_text=json.dumps(
                    match_report_content.__dict__)))
        match_report.encrypted = encrypted_match_report
        if match_report.seen:
            match_report.identifier = None
        match_report.save()
Exemple #11
0
    def test_update_hash_thresholdless(self):

        # we will strip $ to avoid creating a threshold account by accident
        salt = get_random_string(6).strip('$')

        password = '******'
        iterations = 12000

        pbkdf2_encoded_password = pbkdf2(password,
                                         salt,
                                         iterations,
                                         digest=sha256)

        pbkdf2_encoded_password = b64encode(pbkdf2_encoded_password)

        password_normally = make_password(password, salt, hasher='pph')
        password_normally = password_normally.split('$', 4)[4]

        password_through_update = self.hasher.update_hash_thresholdless(
            pbkdf2_encoded_password)

        self.assertTrue(
            constant_time_compare(password_normally, password_through_update))

        # finally, try to log in with our updated password
        pass_string = "{0}${1}${2}${3}${4}".format('pph', 0, iterations, salt,
                                                   password_through_update)

        # TODO we should cehck whether they can provide partial verification
        self.assertTrue(check(password, pass_string))
Exemple #12
0
    def parse_token(self, token):
        """
        Obtain a user from a signed token.

        """
        try:
            data = self.unsign(token)
        except signing.SignatureExpired:
            logger.debug("Expired token: %s", token)
            return
        except signing.BadSignature:
            logger.debug("Bad token: %s", token)
            return
        except Exception:
            logger.exception(
                "Valid signature but unexpected token - if you changed "
                "django-sesame settings, you must regenerate tokens")
            return
        user_pk, data = self.packer.unpack_pk(data)
        user = self.get_user(user_pk)
        if user is None:
            logger.debug("Unknown or inactive user: %s", user_pk)
            return
        h = crypto.pbkdf2(
            self.get_revocation_key(user),
            self.salt,
            self.iterations,
            digest=self.digest,
        )
        if not crypto.constant_time_compare(data, h):
            logger.debug("Invalid token: %s", token)
            return
        logger.debug("Valid token for user %s: %s", user, token)
        return user
Exemple #13
0
    def parse_token(self, token):
        """
        Obtain a user from a signed token.

        """
        try:
            data = self.unsign(token)
        except signing.SignatureExpired:
            logger.debug("Expired token: %s", token)
            return
        except signing.BadSignature:
            logger.debug("Bad token: %s", token)
            return
        except Exception:
            logger.exception(
                "Valid signature but unexpected token - if you changed "
                "django-sesame settings, you must regenerate tokens"
            )
            return
        user_pk, data = self.packer.unpack_pk(data)
        user = self.get_user(user_pk)
        if user is None:
            logger.debug("Unknown or inactive user: %s", user_pk)
            return
        h = crypto.pbkdf2(
            self.get_revocation_key(user),
            self.salt,
            self.iterations,
            digest=self.digest,
        )
        if not crypto.constant_time_compare(data, h):
            logger.debug("Invalid token: %s", token)
            return
        logger.debug("Valid token for user %s: %s", user, token)
        return user
Exemple #14
0
    def parse_token(self, token):
        """
        Obtain a user from a signed token.

        """
        try:
            data = self.unsign(token)
        except signing.SignatureExpired:
            logger.debug("Expired token: %s", token)
            return
        except signing.BadSignature:
            logger.debug("Bad token: %s", token)
            return
        user = self.get_user(*struct.unpack(str('!i'), data[:4]))
        if user is None:
            logger.debug("Unknown token: %s", token)
            return
        h = crypto.pbkdf2(user.password,
                          self.salt,
                          self.iterations,
                          digest=self.digest)
        if not crypto.constant_time_compare(data[4:], h):
            logger.debug("Invalid token: %s", token)
            return
        logger.debug("Valid token for user %s: %s", user, token)
        return user
Exemple #15
0
    def parse_token(self, token):
        """
        Obtain a user from a signed token.

        """
        try:
            data = self.unsign(token)
        except signing.SignatureExpired:
            logger.debug("Expired token: %s", token)
            return
        except signing.BadSignature:
            logger.debug("Bad token: %s", token)
            return
        pk_packer = self.pk_packer()
        user_pk = pk_packer.parse(data[:pk_packer.PACKED_SIZE])
        user = self.get_user(user_pk)
        if user is None:
            logger.debug("Unknown token: %s", token)
            return
        h = crypto.pbkdf2(user.password,
                          self.salt,
                          self.iterations,
                          digest=self.digest)
        if not crypto.constant_time_compare(data[pk_packer.PACKED_SIZE:], h):
            logger.debug("Invalid token: %s", token)
            return
        logger.debug("Valid token for user %s: %s", user, token)
        return user
Exemple #16
0
 def encode(self, password, salt, iterations=None):
     assert password is not None
     assert salt and '$' not in salt
     iterations = iterations or self.iterations
     hash = pbkdf2(password, salt, iterations, digest=self.digest)
     hash = base64.b64encode(hash).decode('ascii').strip()
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
Exemple #17
0
def encrypt_match_report(apps, schema_editor):
    db_alias = schema_editor.connection.alias
    MatchReport = apps.get_model("delivery", "MatchReport")
    for match_report in MatchReport.objects.using(db_alias).all():
        match_report_content = MatchReportContent(
            identifier=match_report.identifier,
            perp_name=match_report.name,
            email=match_report.contact_email,
            phone=match_report.contact_phone,
            contact_name=match_report.contact_name,
            voicemail=match_report.contact_voicemail,
            notes=match_report.contact_notes,
        )
        match_report.salt = get_random_string()
        stretched_identifier = pbkdf2(
            match_report.identifier,
            match_report.salt,
            settings.ORIGINAL_KEY_ITERATIONS,
            digest=hashlib.sha256,
        )
        encrypted_match_report = security.pepper(
            security.encrypt_report(
                stretched_key=stretched_identifier,
                report_text=json.dumps(match_report_content.__dict__),
            ))
        match_report.encrypted = encrypted_match_report
        if match_report.seen:
            match_report.identifier = None
        match_report.save()
 def encode(self, password, salt, iterations=None):
     assert password
     assert salt and '$' not in salt
     if not iterations:
         iterations = self.iterations
     hash = pbkdf2(password, salt, iterations, digest=self.digest)
     hash = hash.encode('base64').strip()
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
Exemple #19
0
def django_check_hash(password, hash):
    if not hash.startswith('pbkdf2_sha256'):
        raise ValueError('Invalid hash ' + hash)
    _type, iterations, salt, hashvalue = hash.split('$', 3)
    stored_hash = b64decode(hashvalue)

    given_hash = pbkdf2(password, salt, int(iterations))
    return constant_time_compare(given_hash, stored_hash)
Exemple #20
0
    def check_password(self, raw_password):
        # Convert Play format to Django format
        iterations, salt, pbkdf2_hash = self.password.split(':')

        new_raw_hash = pbkdf2(raw_password, base64.b16decode(salt.upper()), iterations, 24, hashlib.sha1)
        new_hash = base64.b16encode(new_raw_hash).lower()

        return pbkdf2_hash == new_hash
Exemple #21
0
 def encode(self, password, salt, iterations=None):
     assert password
     assert salt and '$' not in salt
     if not iterations:
         iterations = self.iterations
     hash = pbkdf2(password, salt, iterations, digest=self.digest)
     hash = hash.encode('base64').strip()
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
Exemple #22
0
 def decrypted_report(self, key):
     stretched_key = pbkdf2(key,
                            self.salt,
                            settings.KEY_ITERATIONS,
                            digest=hashlib.sha256)
     box = nacl.secret.SecretBox(stretched_key)
     decrypted = box.decrypt(bytes(self.encrypted))
     return decrypted.decode('utf-8')
def makeCred(account, password):
    cred = Credential()
    cred.prf = "HMAC+SHA256"
    cred.salt = crypto.get_random_string()
    cred.iterations = 20000
    cred.hash = base64.b64encode(crypto.pbkdf2(password, cred.salt, iterations=cred.iterations))
    cred.account = account
    return cred
Exemple #24
0
def django_check_hash(password, hash):
    if not hash.startswith('pbkdf2_sha256'):
        raise ValueError('Invalid hash ' + hash)
    _type, iterations, salt, hashvalue = hash.split('$', 3)
    stored_hash = b64decode(hashvalue)
    
    given_hash = pbkdf2(password, salt, int(iterations))
    return constant_time_compare(given_hash, stored_hash)
 def encode(self, password, salt, iterations=None):
     assert password is not None
     assert salt and "$" not in salt
     if not iterations:
         iterations = self.iterations
     hash = pbkdf2(password, "", iterations, digest=self.digest, dklen=40)
     hash = base64.b64encode(hash)
     return "%s" % hash
Exemple #26
0
 def encode(self, password, salt, iterations=None):
     assert password
     assert salt and "$" not in salt
     if not iterations:
         iterations = self.iterations
     hash = pbkdf2(password, salt, iterations, digest=self.digest)
     hash = base64.b64encode(hash).decode("ascii").strip()
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
Exemple #27
0
def hash_password(raw_password, salt, permutation, iterations=HASH_ITERATIONS):
    """
    Hash data of the form "<raw_password>$<comma sep permutations>"
    """
    permutation = ','.join(map(str, permutation))
    password = '******' % (raw_password, permutation)
    hashed = pbkdf2(password, salt, iterations)
    return base64.b64encode(hashed).decode('ascii').strip()
 def encode(self, password, salt, iterations=None):
     assert password is not None
     assert salt and '$' not in salt
     if not iterations:
         iterations = self.iterations
     hash = pbkdf2(password, salt, iterations, digest=sha256)
     hash = base64.b64encode(hash).decode('ascii').strip()
     return "%s$%d$%s$%s" % ("pbkdf2_sha256", iterations, salt, hash)
Exemple #29
0
 def encode(self, password, salt, iterations=None):
     # Override the standard encoding routine to encode the hash as hex rather than base64 and truncate at 40 chars
     # Alternately, this could run super().encode(), split the result by $, re-encode with hex, and reassemble
     assert password is not None
     assert salt and '$' not in salt
     iterations = iterations or self.iterations
     hash_ = pbkdf2(password, salt, iterations, digest=self.digest)
     hash_hex = hash_.hex()[:40]
     return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash_hex)
 def create_token(self, user):
     """Create a signed token from an `auth.User`."""
     # Include a hash derived from the password so changing the password
     # revokes the token. Usually, user.password will be a secure hash
     # already, but we hash it again in case it isn't. We use MD5
     # to minimize the length of the token. (Remember, if an attacker
     # obtains the URL, he can already log in. This isn't high security.)
     h = crypto.pbkdf2(user.password, 'sesame', 10000, digest=hashlib.md5)
     return self.sign(struct.pack(str('!i'), user.pk) + h)
Exemple #31
0
    def validate_authentication(self, username, password, handler):
        try:
            password_info = self.user_table[username]['pwd'].split('$')
            user_password = base64.b64encode(pbkdf2(password, password_info[2], int(password_info[1])))

            if user_password != password_info[3]:
                raise KeyError
        except KeyError:
            raise AuthenticationFailed
Exemple #32
0
def hash_id_value(hicn):
    """
    Hashes an MBI or HICN to match fhir server logic:
    Both currently use the same hash salt ENV values.
    https://github.com/CMSgov/beneficiary-fhir-data/blob/master/apps/bfd-pipeline/bfd-pipeline-rif-load/src/main/java/gov/cms/bfd/pipeline/rif/load/RifLoader.java#L665-L706
    """
    return binascii.hexlify(
        pbkdf2(hicn, get_user_id_salt(),
               settings.USER_ID_ITERATIONS)).decode("ascii")
Exemple #33
0
 def create_token(self, user):
     """Create a signed token from an `auth.User`."""
     # Include a hash derived from the password so changing the password
     # revokes the token. Usually, user.password will be a secure hash
     # already, but we hash it again in case it isn't. We use MD5
     # to minimize the length of the token. (Remember, if an attacker
     # obtains the URL, he can already log in. This isn't high security.)
     h = crypto.pbkdf2(user.password, 'sesame', 10000, digest=hashlib.md5)
     return self.sign(struct.pack(str('!i'), user.pk) + h)
Exemple #34
0
 def _is_valid_token(self, token, token_str):
     test_token = crypto.pbkdf2(
         token_str,
         token.salt,
         token.iterations
     )
     if test_token == bytes(token.token):
         return True
     return False
Exemple #35
0
 def __init__(self, key, block_size=32):
     self.bs = block_size
     key = key.encode('UTF-8', errors='replace')
     if len(key) >= block_size:
         self.key = key[:block_size]
     else:
         self.key = self._pad(key)
     self.key_hash = crypto.pbkdf2(key, key[:10], 10000, 0)
     if len(self.key_hash) < AES.block_size:
         self.key_hash += b'0' * (AES.block_size - len(self.key_hash))
Exemple #36
0
    def generate_token(self):
        # Inspired by: https://github.com/aaugustin/django-sesame/

        raw_token = struct.pack(
            str("!i"), self.invited_user.pk) + crypto.pbkdf2(
                self.invited_user.email, "radiologo", 10000)
        url_ready_token = signing.TimestampSigner(salt="radiologo").sign(
            signing.b64_encode(raw_token).decode())
        self.sent_token = url_ready_token
        return url_ready_token
Exemple #37
0
 def test_default_hmac_alg(self):
     kwargs = {
         'password': b'password',
         'salt': b'salt',
         'iterations': 1,
         'dklen': 20
     }
     self.assertEqual(
         pbkdf2(**kwargs),
         hashlib.pbkdf2_hmac(hash_name=hashlib.sha256().name, **kwargs))
Exemple #38
0
def hexlify_pbkdf2(password, salt, iterations, length, digest=hashlib.sha1):
    """
    >>> hash = hexlify_pbkdf2("not secret", "a salt value", iterations=100, length=16)
    >>> hash == '0b919231515dde16f76364666cf07107'
    True
    """
    # log.debug("hexlify_pbkdf2 with iterations=%i", iterations)
    hash = crypto.pbkdf2(password, salt, iterations=iterations, dklen=length, digest=digest)
    hash = binascii.hexlify(hash)
    hash = six.text_type(hash, "ascii")
    return hash
Exemple #39
0
 def test_default_hmac_alg(self):
     kwargs = {
         "password": b"password",
         "salt": b"salt",
         "iterations": 1,
         "dklen": 20,
     }
     self.assertEqual(
         pbkdf2(**kwargs),
         hashlib.pbkdf2_hmac(hash_name=hashlib.sha256().name, **kwargs),
     )
Exemple #40
0
 def encrypt_report(self, report_text, key):
     if not self.salt:
         self.salt = get_random_string()
     else:
         self.last_edited = timezone.now()
     stretched_key = pbkdf2(key, self.salt, settings.KEY_ITERATIONS, digest=hashlib.sha256)
     box = nacl.secret.SecretBox(stretched_key)
     message = report_text.encode('utf-8')
     nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
     encrypted = box.encrypt(message, nonce)
     self.encrypted = encrypted
Exemple #41
0
def verify_pw(request):
    if request.method == 'POST':
        if check_password(request.POST.get("Password"), request.user.password):
            _, _, salt, _ = request.user.password.split('$')
            key = pbkdf2(request.POST.get("Password"), salt, 50000, 48)
            request.session['iv'] = key[0:16].hex()
            request.session['cipherKey'] = key[16:].hex()
            return redirect('/password/')
        else:
            messages.error(request, 'Incorrect Password')
    return render(request, "password/verify_pw.html", {})
def hash_hicn(hicn):
    """
    Hashes a hicn to match fhir server logic:
    https://github.com/CMSgov/beneficiary-fhir-data/blob/master/apps/bfd-pipeline/bfd-pipeline-rif-load/src/main/java/gov/cms/bfd/pipeline/rif/load/RifLoader.java#L665-L706

    """
    assert hicn != "", "HICN cannot be the empty string"

    return binascii.hexlify(pbkdf2(hicn,
                            get_user_id_salt(),
                            settings.USER_ID_ITERATIONS)).decode("ascii")
    def _get_password_hash(self, password, fixed_salt=None):
        """ Return password after hash operation

        :param password: String with plain text password
        :param fixed_salt: If Set, use the given fixed_salt in pbkdf2 operation instead of computing a new one
        This is used to compare existing password
        :return: String with hashed password
        """
        if self.password_salt is not None:
            salt = self.password_salt
            if self.password_salt_position == 'begin':
                password = "******".format(salt, password)
            elif self.password_salt_position == 'end':
                password = "******".format(password, salt)

        hash_algo = self.password_hash_algo
        if not isinstance(password, bytes):
            password = str(password).encode('utf8')
        if hash_algo == 'plain':
            return password
        elif hash_algo == 'md5':
            return hashlib.md5(password).hexdigest()
        elif hash_algo == 'sha1':
            return hashlib.sha1(password).hexdigest()
        elif hash_algo == 'sha256':
            return hashlib.sha256(password).hexdigest()
        elif hash_algo == 'sha384':
            return hashlib.sha384(password).hexdigest()
        elif hash_algo == 'sha512':
            return hashlib.sha512(password).hexdigest()
        elif hash_algo == 'make_password':
            return make_password(password)
        elif hash_algo.startswith('pbkdf2'):
            args = hash_algo.split('-')
            hash = args[1]
            iter = args[2]

            for alg in hashlib.algorithms_available:
                if str(hash) in alg:
                    algo = getattr(hashlib, 'sha' + str(hash))
                    if not fixed_salt:
                        rdm_str = get_random_string()
                    else:
                        rdm_str = fixed_salt
                    pwd = base64.b64encode(
                        pbkdf2(password, rdm_str, int(iter),
                               digest=algo)).decode('utf8')
                    return "pbkdf2_sha" + str(hash) + '$' + str(
                        iter) + '$' + rdm_str + '$' + pwd

        else:
            raise NotImplemented("Password hash algorithm not implemented, "
                                 "algo: {}".format(hash_algo))
Exemple #44
0
    def get_bucket_key(self, *args, **kwargs):
        ip_address = super(HashedRemoteIP,
                           self).get_bucket_key(*args, **kwargs)

        bucket_key = pbkdf2(ip_address,
                            self.get_salt(),
                            self.get_iterations(),
                            digest=hashlib.sha256)

        bucket_key = binascii.b2a_hex(bucket_key)

        return bucket_key
Exemple #45
0
    def post(self, request):
        phone_number = str(self.request.data.get('phone_number', '')).strip()
        search_for_code = str(self.request.data.get('code', '')).strip()
        codes = SMSAuthCode.objects.filter(phone_number=phone_number).all()
        for code in codes:
            test_code = crypto.pbkdf2(
                search_for_code,
                code.salt,
                code.iterations
            )
            if test_code == bytes(code.code):
                if code.expires < datetime.now():
                    code.delete()
                    return Response({'error': 'code expired'}, status=403)

                auth_token = SMSAuthToken.generate_token()
                salt = crypto.get_random_string()
                iterations = randint(10000, 10100)
                encrypted = crypto.pbkdf2(auth_token, salt, iterations)

                token = SMSAuthToken.objects.create(
                    phone_number=phone_number,
                    token=encrypted,
                    salt=salt,
                    iterations=iterations
                )
                for user in code.users.all():
                    token.users.add(user)

                serialized_users = [
                    UserSerializer(u).data
                    for u
                    in code.users.all()
                ]
                code.delete()
                return Response({
                    'users': serialized_users,
                    'token': auth_token,
                })
        return Response({'error': 'invalid'}, status=403)
Exemple #46
0
    def create_token(self, user):
        """
        Create a signed token from a user.

        """
        # Include a hash derived from the password so changing the password
        # revokes the token. Usually, user.password will be a secure hash
        # already, but we hash it again in case it isn't. We default to MD5
        # to minimize the length of the token. (Remember, if an attacker
        # obtains the URL, he can already log in. This isn't high security.)
        h = crypto.pbkdf2(
            user.password, self.salt, self.iterations, digest=self.digest)
        return self.sign(struct.pack(str('!i'), user.pk) + h)
Exemple #47
0
    def begin_two_factor_authentication(self, session):
        # Generate a TFA key and salt, store them for later verification
        tfa_key = str(uuid.uuid4())
        tfa_salt = str(uuid.uuid4())
        session['tfa_key'] = tfa_key
        session['tfa_salt'] = tfa_salt

        # Generate the signature to send back to the user
        tfa_sig = base64.b16encode(pbkdf2(tfa_key + self.password, tfa_salt, 1000, 24))

        return {
            'status': 'tfa',
            'tfa_userid': self.id,
            'tfa_signature': tfa_sig
        }
Exemple #48
0
    def create_token(self, user):
        """
        Create a signed token from a user.

        """
        # The password is expected to be a secure hash but we hash it again
        # for additional safety. We default to MD5 to minimize the length of
        # the token. (Remember, if an attacker obtains the URL, he can already
        # log in. This isn't high security.)
        h = crypto.pbkdf2(
            self.get_revocation_key(user),
            self.salt,
            self.iterations,
            digest=self.digest,
        )
        return self.sign(self.packer.pack_pk(user.pk) + h)
Exemple #49
0
 def parse_token(self, token):
     """Obtain an `auth.User` from a signed token."""
     try:
         data = self.unsign(token)
     except signing.BadSignature:
         logger.debug("Invalid token: %s", token)
         return
     user = self.get_user(*struct.unpack(str('!i'), data[:4]))
     if user is None:
         logger.debug("Unknown token: %s", token)
         return
     h = crypto.pbkdf2(user.password, 'sesame', 10000, digest=hashlib.md5)
     if not crypto.constant_time_compare(data[4:], h):
         logger.debug("Expired token: %s", token)
         return
     logger.debug("Valid token for user %s: %s", user, token)
     return user
Exemple #50
0
def _encrypt_report(salt, key, report_text):
    """Encrypts a report using the given secret key & salt. The secret key is stretched to 32 bytes using Django's
    PBKDF2+SHA256 implementation. The encryption uses PyNacl & Salsa20 stream cipher.

    Args:
      salt (str): cryptographic salt
      key (str): secret key
      report_text (str): full report as a string

    Returns:
      bytes: the encrypted bytes of the report

    """
    stretched_key = pbkdf2(key, salt, settings.KEY_ITERATIONS, digest=hashlib.sha256)
    box = nacl.secret.SecretBox(stretched_key)
    message = report_text.encode('utf-8')
    nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
    return box.encrypt(message, nonce)
Exemple #51
0
def _decrypt_report(salt, key, encrypted):
    """Decrypts an encrypted report.

    Args:
      salt (str): cryptographic salt
      key (str): secret key
      encrypted (bytes): full report encrypted

    Returns:
      str: the decrypted report as a string

    Raises:
      CryptoError: If the key and salt fail to decrypt the record.

    """
    stretched_key = pbkdf2(key, salt, settings.KEY_ITERATIONS, digest=hashlib.sha256)
    box = nacl.secret.SecretBox(stretched_key)
    decrypted = box.decrypt(bytes(encrypted))  # need to force to bytes bc BinaryField can return as memoryview
    return decrypted.decode('utf-8')
Exemple #52
0
 def _is_valid_token(self, request, token, password, user_id):
     test_token = crypto.pbkdf2(
         password,
         token.salt,
         token.iterations
     )
     if test_token == bytes(token.token):
         if token.expires < datetime.now():
             return False
         if token.users.count() == 1:
             request.user = token.users.all()[0]
             return True
         elif token.users.count() > 1:
             if not user_id:
                 return False
             user = [u for u in token.users.all() if u.id == user_id]
             if len(user) == 1:
                 request.user = user[0]
                 return True
             return False
Exemple #53
0
def decrypt_secret(secret, password, username):
    """
    We will get passed in a 16 byte block of data
    that has been enrypted with AES using the PIN
    the first 10 bytes are the secret
    and the remaining 6 are padding

    :param secret: The secret.
    :type secret: str
    :param password: The password.
    :type password: str
    :param username: The user this is for.
    :type username: str
    :returns: str
    """

    secret = base64.b32decode(secret)
    pw_hash = pbkdf2(password.encode('ascii'), username.encode('ascii'), 10000)
    cipher = AES.new(pw_hash, AES.MODE_ECB, '')
    tmp = cipher.decrypt(secret)
    return tmp[:10]
Exemple #54
0
def decrypt_secret(secret, password, username):
    """
    We will get passed in a 16 byte block of data
    that has been enrypted with AES using the PIN
    the first 10 bytes are the secret
    and the remaining 6 are padding

    :param secret: The secret.
    :type secret: str
    :param password: The password.
    :type password: str
    :param username: The user this is for.
    :type username: str
    :returns: str
    """

    secret = base64.b32decode(secret)
    pw_hash = pbkdf2(password.encode('ascii'), username.encode('ascii'), 10000)
    crypt_object = EVP.Cipher('aes_128_ecb', pw_hash, '', 0, padding=0)
    tmp = crypt_object.update(secret)
    return tmp[:10]
    def authenticate(self, session=None, user_id=None, token=None, signature=None):
        # Ensure that we have TFA parameters
        if token is None or signature is None:
            return None

        # Fetch the user
        try:
            user = User.objects.get(pk=user_id)
        except:
            return None

        # Verify the signature from the user is the one we generated
        expected_tfa_sig = base64.b16encode(pbkdf2(session.get('tfa_key', '') + user.password, session.get('tfa_salt', ''), 1000, 24))
        if expected_tfa_sig != signature:
            return None

        # Verify the token from the user
        expected_token = str(get_totp_token(user.totp_secret.upper())).zfill(6)
        if expected_token == token:
            return user

        return None
Exemple #56
0
def encrypt_secret(secret, password, username):
    """
    Pad the secret with 6 random bytes
    which makes the secret 16 bytes for ECB
    but also makes all encrypted secrets unique
    even if they have the same original value
    which is important for cases where we might allow
    a user to carry over an existing secret from
    another system

    :param secret: The secret.
    :type secret: str
    :param password: The password.
    :type password: str
    :param username: The user this is for.
    :type username: str
    :returns: str
    """

    secret += gen_random(6)
    pw_hash = pbkdf2(password.encode('ascii'), username.encode('ascii'), 10000)
    cipher = AES.new(pw_hash, AES.MODE_ECB, '')
    tmp = cipher.encrypt(secret)
    return tmp
Exemple #57
0
def encrypt_secret(secret, password, username):
    """
    Pad the secret with 6 random bytes
    which makes the secret 16 bytes for ECB
    but also makes all encrypted secrets unique
    even if they have the same oroginal value
    which is important for cases where we might allow
    a user to carry over an existing secret from
    another system

    :param secret: The secret.
    :type secret: str
    :param password: The password.
    :type password: str
    :param username: The user this is for.
    :type username: str
    :returns: str
    """

    secret += gen_random(6)
    pw_hash = pbkdf2(password.encode('ascii'), username.encode('ascii'), 10000)
    crypt_object = EVP.Cipher('aes_128_ecb', pw_hash, '', 1, padding=0)
    tmp = crypt_object.update(secret)
    return tmp