1212import traceback
1313import warnings
1414
15+ import argon2
16+ import argon2 .exceptions
17+ from argon2 import PasswordHasher
1518from ipython_genutils .py3compat import cast_bytes , str_to_bytes , cast_unicode
1619from traitlets .config import Config , ConfigFileNotFound , JSONFileConfigLoader
1720from jupyter_core .paths import jupyter_config_dir
2124salt_len = 12
2225
2326
24- def passwd (passphrase = None , algorithm = 'sha1 ' ):
27+ def passwd (passphrase = None , algorithm = 'argon2 ' ):
2528 """Generate hashed password and salt for use in server configuration.
2629
2730 In the server configuration, set `c.ServerApp.password` to
@@ -34,7 +37,7 @@ def passwd(passphrase=None, algorithm='sha1'):
3437 and verify a password.
3538 algorithm : str
3639 Hashing algorithm to use (e.g, 'sha1' or any argument supported
37- by :func:`hashlib.new`).
40+ by :func:`hashlib.new`, or 'argon2' ).
3841
3942 Returns
4043 -------
@@ -59,6 +62,16 @@ def passwd(passphrase=None, algorithm='sha1'):
5962 else :
6063 raise ValueError ('No matching passwords found. Giving up.' )
6164
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+
6275 h = hashlib .new (algorithm )
6376 salt = ('%0' + str (salt_len ) + 'x' ) % random .getrandbits (4 * salt_len )
6477 h .update (cast_bytes (passphrase , 'utf-8' ) + str_to_bytes (salt , 'ascii' ))
@@ -84,14 +97,24 @@ def passwd_check(hashed_passphrase, passphrase):
8497 Examples
8598 --------
8699 >>> from jupyter_server.auth.security import passwd_check
87- >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
88- ... 'mypassword')
100+ >>> passwd_check('argon2:...', 'mypassword')
89101 True
90102
91- >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
92- ... 'anotherpassword')
103+ >>> passwd_check('argon2:...', 'otherpassword')
93104 False
105+
106+ >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
107+ ... 'mypassword')
108+ True
94109 """
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+
95118 try :
96119 algorithm , salt , pw_digest = hashed_passphrase .split (':' , 2 )
97120 except (ValueError , TypeError ):
0 commit comments