security.py
118 lines
| 3.4 KiB
| text/x-python
|
PythonLexer
Stefan van der Walt
|
r5320 | """ | ||
Password generation for the IPython notebook. | ||||
""" | ||||
Fernando Perez
|
r5337 | #----------------------------------------------------------------------------- | ||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
# Stdlib | ||||
import getpass | ||||
Stefan van der Walt
|
r5320 | import hashlib | ||
import random | ||||
Fernando Perez
|
r5337 | # Our own | ||
Stefan van der Walt
|
r5335 | from IPython.core.error import UsageError | ||
Fernando Perez
|
r5337 | from IPython.testing.skipdoctest import skip_doctest | ||
Thomas Kluyver
|
r5343 | from IPython.utils.py3compat import cast_bytes, str_to_bytes | ||
Fernando Perez
|
r5337 | |||
#----------------------------------------------------------------------------- | ||||
# Globals | ||||
#----------------------------------------------------------------------------- | ||||
Stefan van der Walt
|
r5335 | |||
Stefan van der Walt
|
r5332 | # Length of the salt in nr of hex chars, which implies salt_len * 4 | ||
# bits of randomness. | ||||
salt_len = 12 | ||||
Fernando Perez
|
r5337 | #----------------------------------------------------------------------------- | ||
# Functions | ||||
#----------------------------------------------------------------------------- | ||||
@skip_doctest | ||||
Stefan van der Walt
|
r5334 | def passwd(passphrase=None, algorithm='sha1'): | ||
Stefan van der Walt
|
r5320 | """Generate hashed password and salt for use in notebook configuration. | ||
Stefan van der Walt
|
r5322 | In the notebook configuration, set `c.NotebookApp.password` to | ||
the generated string. | ||||
Stefan van der Walt
|
r5320 | Parameters | ||
---------- | ||||
passphrase : str | ||||
Stefan van der Walt
|
r5333 | Password to hash. If unspecified, the user is asked to input | ||
and verify a password. | ||||
Stefan van der Walt
|
r5328 | algorithm : str | ||
Stefan van der Walt
|
r5331 | Hashing algorithm to use (e.g, 'sha1' or any argument supported | ||
by :func:`hashlib.new`). | ||||
Stefan van der Walt
|
r5320 | |||
Returns | ||||
------- | ||||
hashed_passphrase : str | ||||
Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'. | ||||
Examples | ||||
-------- | ||||
In [1]: passwd('mypassword') | ||||
Out[1]: 'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12' | ||||
""" | ||||
Stefan van der Walt
|
r5334 | if passphrase is None: | ||
for i in range(3): | ||||
p0 = getpass.getpass('Enter password: ') | ||||
p1 = getpass.getpass('Verify password: ') | ||||
if p0 == p1: | ||||
passphrase = p0 | ||||
break | ||||
else: | ||||
print('Passwords do not match.') | ||||
Stefan van der Walt
|
r5333 | else: | ||
Stefan van der Walt
|
r5335 | raise UsageError('No matching passwords found. Giving up.') | ||
Stefan van der Walt
|
r5333 | |||
Stefan van der Walt
|
r5320 | h = hashlib.new(algorithm) | ||
Stefan van der Walt
|
r5332 | salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len) | ||
Thomas Kluyver
|
r5343 | h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii')) | ||
Stefan van der Walt
|
r5320 | |||
return ':'.join((algorithm, salt, h.hexdigest())) | ||||
Fernando Perez
|
r5337 | |||
Stefan van der Walt
|
r5320 | def passwd_check(hashed_passphrase, passphrase): | ||
"""Verify that a given passphrase matches its hashed version. | ||||
Parameters | ||||
---------- | ||||
hashed_passphrase : str | ||||
Hashed password, in the format returned by `passwd`. | ||||
passphrase : str | ||||
Passphrase to validate. | ||||
Returns | ||||
------- | ||||
valid : bool | ||||
True if the passphrase matches the hash. | ||||
Examples | ||||
-------- | ||||
In [1]: from IPython.lib.security import passwd_check | ||||
Fernando Perez
|
r5337 | In [2]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a', | ||
Stefan van der Walt
|
r5320 | ...: 'mypassword') | ||
Out[2]: True | ||||
Fernando Perez
|
r5337 | In [3]: passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a', | ||
Stefan van der Walt
|
r5320 | ...: 'anotherpassword') | ||
Out[3]: False | ||||
""" | ||||
try: | ||||
algorithm, salt, pw_digest = hashed_passphrase.split(':', 2) | ||||
except (ValueError, TypeError): | ||||
return False | ||||
Stefan van der Walt
|
r5328 | try: | ||
h = hashlib.new(algorithm) | ||||
except ValueError: | ||||
return False | ||||
Fernando Perez
|
r5338 | if len(pw_digest) == 0: | ||
Stefan van der Walt
|
r5320 | return False | ||
Thomas Kluyver
|
r13558 | h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii')) | ||
Stefan van der Walt
|
r5320 | |||
return h.hexdigest() == pw_digest | ||||