12
12
import traceback
13
13
import warnings
14
14
15
+ import argon2
16
+ import argon2 .exceptions
17
+ from argon2 import PasswordHasher
15
18
from ipython_genutils .py3compat import cast_bytes , str_to_bytes , cast_unicode
16
19
from traitlets .config import Config , ConfigFileNotFound , JSONFileConfigLoader
17
20
from jupyter_core .paths import jupyter_config_dir
21
24
salt_len = 12
22
25
23
26
24
- def passwd (passphrase = None , algorithm = 'sha1 ' ):
27
+ def passwd (passphrase = None , algorithm = 'argon2 ' ):
25
28
"""Generate hashed password and salt for use in server configuration.
26
29
27
30
In the server configuration, set `c.ServerApp.password` to
@@ -34,7 +37,7 @@ def passwd(passphrase=None, algorithm='sha1'):
34
37
and verify a password.
35
38
algorithm : str
36
39
Hashing algorithm to use (e.g, 'sha1' or any argument supported
37
- by :func:`hashlib.new`).
40
+ by :func:`hashlib.new`, or 'argon2' ).
38
41
39
42
Returns
40
43
-------
@@ -59,6 +62,16 @@ def passwd(passphrase=None, algorithm='sha1'):
59
62
else :
60
63
raise ValueError ('No matching passwords found. Giving up.' )
61
64
65
+ if algorithm == 'argon2' :
66
+ ph = PasswordHasher (
67
+ memory_cost = 10240 ,
68
+ time_cost = 10 ,
69
+ parallelism = 8 ,
70
+ )
71
+ h = ph .hash (passphrase )
72
+
73
+ return ':' .join ((algorithm , cast_unicode (h , 'ascii' )))
74
+
62
75
h = hashlib .new (algorithm )
63
76
salt = ('%0' + str (salt_len ) + 'x' ) % random .getrandbits (4 * salt_len )
64
77
h .update (cast_bytes (passphrase , 'utf-8' ) + str_to_bytes (salt , 'ascii' ))
@@ -84,14 +97,24 @@ def passwd_check(hashed_passphrase, passphrase):
84
97
Examples
85
98
--------
86
99
>>> from jupyter_server.auth.security import passwd_check
87
- >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
88
- ... 'mypassword')
100
+ >>> passwd_check('argon2:...', 'mypassword')
89
101
True
90
102
91
- >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
92
- ... 'anotherpassword')
103
+ >>> passwd_check('argon2:...', 'otherpassword')
93
104
False
105
+
106
+ >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
107
+ ... 'mypassword')
108
+ True
94
109
"""
110
+ if hashed_passphrase .startswith ('argon2:' ):
111
+ ph = argon2 .PasswordHasher ()
112
+
113
+ try :
114
+ return ph .verify (hashed_passphrase [7 :], passphrase )
115
+ except argon2 .exceptions .VerificationError :
116
+ return False
117
+
95
118
try :
96
119
algorithm , salt , pw_digest = hashed_passphrase .split (':' , 2 )
97
120
except (ValueError , TypeError ):
0 commit comments