-
Notifications
You must be signed in to change notification settings - Fork 3
/
flask_scrypt.py
121 lines (95 loc) · 3.43 KB
/
flask_scrypt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Flask-Scrypt flask extension provides scrypt password hashing and random salt generation.
Hashes and Salts are base64 encoded.
"""
from __future__ import print_function, unicode_literals
import sys
import base64
import hmac
from os import urandom
from werkzeug.security import safe_str_cmp
try:
from itertools import izip
except ImportError:
izip = zip
_builtin_safe_str_cmp = getattr(hmac, 'compare_digest', None)
__version_info__ = ('0', '1', '3', '6')
__version__ = '.'.join(__version_info__)
__author__ = 'Gilbert Robinson'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2013 Gilbert Robinson, Copyright (c) 2014 Samuel Marks'
__all__ = ['generate_password_hash', 'generate_random_salt',
'check_password_hash', 'enbase64', 'debase64']
try:
from scrypt import hash as scrypt_hash
except ImportError as err:
print('Please install py-scrypt package. Error: ', err)
raise err
PYTHON2 = sys.version_info < (3, 0)
def enbase64(byte_str):
"""
Encode bytes/strings to base64.
Args:
- ``byte_str``: The string or bytes to base64 encode.
Returns:
- byte_str encoded as base64.
"""
# Python 3: base64.b64encode() expects type byte
if isinstance(byte_str, str) and not PYTHON2:
byte_str = bytes(byte_str, 'utf-8')
return base64.b64encode(byte_str)
def debase64(byte_str):
"""
Decode base64 encoded bytes/strings.
Args:
- ``byte_str``: The string or bytes to base64 encode.
Returns:
- decoded string as type str for python2 and type byte for python3.
"""
# Python 3: base64.b64decode() expects type byte
if isinstance(byte_str, str) and not PYTHON2:
byte_str = bytes(byte_str, 'utf-8')
return base64.b64decode(byte_str)
def generate_password_hash(password, salt, N=1 << 14, r=8, p=1, buflen=64):
"""
Generate password hash givin the password string and salt.
Args:
- ``password``: Password string.
- ``salt`` : Random base64 encoded string.
Optional args:
- ``N`` : the CPU cost, must be a power of 2 greater than 1, defaults to 1 << 14.
- ``r`` : the memory cost, defaults to 8.
- ``p`` : the parallelization parameter, defaults to 1.
The parameters r, p, and buflen must satisfy r * p < 2^30 and
buflen <= (2^32 - 1) * 32.
The recommended parameters for interactive logins as of 2009 are N=16384,
r=8, p=1. Remember to use a good random salt.
Returns:
- base64 encoded scrypt hash.
"""
if PYTHON2:
password = password.encode('utf-8')
salt = salt.encode('utf-8')
pw_hash = scrypt_hash(password, salt, N, r, p, buflen)
return enbase64(pw_hash)
def generate_random_salt(byte_size=64):
"""
Generate random salt to use with generate_password_hash().
Optional Args:
- ``byte_size``: The length of salt to return. default = 64.
Returns:
- str of base64 encoded random bytes.
"""
return enbase64(urandom(byte_size))
def check_password_hash(password, password_hash, salt, N=1 << 14, r=8, p=1, buflen=64):
"""
Given a password, hash, salt this function verifies the password is equal to hash/salt.
Args:
- ``password``: The password to perform check on.
Returns:
- ``bool``
"""
candidate_hash = generate_password_hash(password, salt, N, r, p, buflen)
return safe_str_cmp(password_hash, candidate_hash)