##// END OF EJS Templates
Merge pull request #4526 from takluyver/passwd_check_unicode...
Min RK -
r13562:3ef54c32 merge
parent child Browse files
Show More
@@ -1,118 +1,118
1 """
1 """
2 Password generation for the IPython notebook.
2 Password generation for the IPython notebook.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Stdlib
7 # Stdlib
8 import getpass
8 import getpass
9 import hashlib
9 import hashlib
10 import random
10 import random
11
11
12 # Our own
12 # Our own
13 from IPython.core.error import UsageError
13 from IPython.core.error import UsageError
14 from IPython.testing.skipdoctest import skip_doctest
14 from IPython.testing.skipdoctest import skip_doctest
15 from IPython.utils.py3compat import cast_bytes, str_to_bytes
15 from IPython.utils.py3compat import cast_bytes, str_to_bytes
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Globals
18 # Globals
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 # Length of the salt in nr of hex chars, which implies salt_len * 4
21 # Length of the salt in nr of hex chars, which implies salt_len * 4
22 # bits of randomness.
22 # bits of randomness.
23 salt_len = 12
23 salt_len = 12
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Functions
26 # Functions
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 @skip_doctest
29 @skip_doctest
30 def passwd(passphrase=None, algorithm='sha1'):
30 def passwd(passphrase=None, algorithm='sha1'):
31 """Generate hashed password and salt for use in notebook configuration.
31 """Generate hashed password and salt for use in notebook configuration.
32
32
33 In the notebook configuration, set `c.NotebookApp.password` to
33 In the notebook configuration, set `c.NotebookApp.password` to
34 the generated string.
34 the generated string.
35
35
36 Parameters
36 Parameters
37 ----------
37 ----------
38 passphrase : str
38 passphrase : str
39 Password to hash. If unspecified, the user is asked to input
39 Password to hash. If unspecified, the user is asked to input
40 and verify a password.
40 and verify a password.
41 algorithm : str
41 algorithm : str
42 Hashing algorithm to use (e.g, 'sha1' or any argument supported
42 Hashing algorithm to use (e.g, 'sha1' or any argument supported
43 by :func:`hashlib.new`).
43 by :func:`hashlib.new`).
44
44
45 Returns
45 Returns
46 -------
46 -------
47 hashed_passphrase : str
47 hashed_passphrase : str
48 Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'.
48 Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'.
49
49
50 Examples
50 Examples
51 --------
51 --------
52 In [1]: passwd('mypassword')
52 In [1]: passwd('mypassword')
53 Out[1]: 'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12'
53 Out[1]: 'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12'
54
54
55 """
55 """
56 if passphrase is None:
56 if passphrase is None:
57 for i in range(3):
57 for i in range(3):
58 p0 = getpass.getpass('Enter password: ')
58 p0 = getpass.getpass('Enter password: ')
59 p1 = getpass.getpass('Verify password: ')
59 p1 = getpass.getpass('Verify password: ')
60 if p0 == p1:
60 if p0 == p1:
61 passphrase = p0
61 passphrase = p0
62 break
62 break
63 else:
63 else:
64 print('Passwords do not match.')
64 print('Passwords do not match.')
65 else:
65 else:
66 raise UsageError('No matching passwords found. Giving up.')
66 raise UsageError('No matching passwords found. Giving up.')
67
67
68 h = hashlib.new(algorithm)
68 h = hashlib.new(algorithm)
69 salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
69 salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
70 h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii'))
70 h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii'))
71
71
72 return ':'.join((algorithm, salt, h.hexdigest()))
72 return ':'.join((algorithm, salt, h.hexdigest()))
73
73
74
74
75 def passwd_check(hashed_passphrase, passphrase):
75 def passwd_check(hashed_passphrase, passphrase):
76 """Verify that a given passphrase matches its hashed version.
76 """Verify that a given passphrase matches its hashed version.
77
77
78 Parameters
78 Parameters
79 ----------
79 ----------
80 hashed_passphrase : str
80 hashed_passphrase : str
81 Hashed password, in the format returned by `passwd`.
81 Hashed password, in the format returned by `passwd`.
82 passphrase : str
82 passphrase : str
83 Passphrase to validate.
83 Passphrase to validate.
84
84
85 Returns
85 Returns
86 -------
86 -------
87 valid : bool
87 valid : bool
88 True if the passphrase matches the hash.
88 True if the passphrase matches the hash.
89
89
90 Examples
90 Examples
91 --------
91 --------
92 In [1]: from IPython.lib.security import passwd_check
92 In [1]: from IPython.lib.security import passwd_check
93
93
94 In [2]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
94 In [2]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
95 ...: 'mypassword')
95 ...: 'mypassword')
96 Out[2]: True
96 Out[2]: True
97
97
98 In [3]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
98 In [3]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
99 ...: 'anotherpassword')
99 ...: 'anotherpassword')
100 Out[3]: False
100 Out[3]: False
101
101
102 """
102 """
103 try:
103 try:
104 algorithm, salt, pw_digest = hashed_passphrase.split(':', 2)
104 algorithm, salt, pw_digest = hashed_passphrase.split(':', 2)
105 except (ValueError, TypeError):
105 except (ValueError, TypeError):
106 return False
106 return False
107
107
108 try:
108 try:
109 h = hashlib.new(algorithm)
109 h = hashlib.new(algorithm)
110 except ValueError:
110 except ValueError:
111 return False
111 return False
112
112
113 if len(pw_digest) == 0:
113 if len(pw_digest) == 0:
114 return False
114 return False
115
115
116 h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii'))
116 h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii'))
117
117
118 return h.hexdigest() == pw_digest
118 return h.hexdigest() == pw_digest
@@ -1,21 +1,26
1 # coding: utf-8
1 from IPython.lib import passwd
2 from IPython.lib import passwd
2 from IPython.lib.security import passwd_check, salt_len
3 from IPython.lib.security import passwd_check, salt_len
3 import nose.tools as nt
4 import nose.tools as nt
4
5
5 def test_passwd_structure():
6 def test_passwd_structure():
6 p = passwd('passphrase')
7 p = passwd('passphrase')
7 algorithm, salt, hashed = p.split(':')
8 algorithm, salt, hashed = p.split(':')
8 nt.assert_equal(algorithm, 'sha1')
9 nt.assert_equal(algorithm, 'sha1')
9 nt.assert_equal(len(salt), salt_len)
10 nt.assert_equal(len(salt), salt_len)
10 nt.assert_equal(len(hashed), 40)
11 nt.assert_equal(len(hashed), 40)
11
12
12 def test_roundtrip():
13 def test_roundtrip():
13 p = passwd('passphrase')
14 p = passwd('passphrase')
14 nt.assert_equal(passwd_check(p, 'passphrase'), True)
15 nt.assert_equal(passwd_check(p, 'passphrase'), True)
15
16
16 def test_bad():
17 def test_bad():
17 p = passwd('passphrase')
18 p = passwd('passphrase')
18 nt.assert_equal(passwd_check(p, p), False)
19 nt.assert_equal(passwd_check(p, p), False)
19 nt.assert_equal(passwd_check(p, 'a:b:c:d'), False)
20 nt.assert_equal(passwd_check(p, 'a:b:c:d'), False)
20 nt.assert_equal(passwd_check(p, 'a:b'), False)
21 nt.assert_equal(passwd_check(p, 'a:b'), False)
21
22
23 def test_passwd_check_unicode():
24 # GH issue #4524
25 phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f'
26 assert passwd_check(phash, u"łe¶ŧ←↓→") No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now