forked from pyca/bcrypt
/
__init__.py
99 lines (76 loc) · 3.03 KB
/
__init__.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
# Author:: Donald Stufft (<donald@stufft.io>)
# Copyright:: Copyright (c) 2013 Donald Stufft
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import hashlib
import os
import sys
from cffi import FFI
from . import __about__
from .__about__ import *
__all__ = ["gensalt", "hashpw"] + __about__.__all__
# True if we are running on Python 3.
PY3 = sys.version_info[0] == 3
if PY3:
text_type = str
else:
text_type = unicode
_crypt_blowfish_dir = "crypt_blowfish-1.2"
_bundled_dir = os.path.join(os.path.dirname(__file__), _crypt_blowfish_dir)
_ffi = FFI()
_ffi.cdef("""
char *crypt_gensalt_rn(const char *prefix, unsigned long count,
const char *input, int size, char *output, int output_size);
char *crypt_rn(const char *key, const char *setting, void *data, int size);
""")
_bcrypt_lib = _ffi.verify('#include "ow-crypt.h"',
sources=[
str(os.path.join(_bundled_dir, "crypt_blowfish.c")),
str(os.path.join(_bundled_dir, "crypt_gensalt.c")),
str(os.path.join(_bundled_dir, "wrapper.c")),
# How can we get distutils to work with a .S file?
# Set https://github.com/dstufft/bcrypt/blob/4c939e895bd9607301cda6d6f05ef3c1146eb658/bcrypt/crypt_blowfish-1.2/crypt_blowfish.c#L57
# back to 1 if we get ASM loaded.
# str(os.path.join(_bundled_dir, "x86.S")),
],
include_dirs=[str(_bundled_dir)],
modulename=str("_".join([
"_cffi",
hashlib.sha1(
"".join(_ffi._cdefsources).encode("utf-8")
).hexdigest()[:6],
hashlib.sha1(
_crypt_blowfish_dir.encode("utf-8")
).hexdigest()[:6],
])),
)
def gensalt(rounds=12):
salt = os.urandom(16)
output = _ffi.new("unsigned char[]", 30)
retval = _bcrypt_lib.crypt_gensalt_rn(
b"$2a$", rounds, salt, len(salt), output, len(output))
if not retval:
raise ValueError("Invalid rounds")
return _ffi.string(output)
def hashpw(password, salt):
if isinstance(password, text_type) or isinstance(salt, text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
hashed = _ffi.new("unsigned char[]", 128)
retval = _bcrypt_lib.crypt_rn(password, salt, hashed, len(hashed))
if not retval:
raise ValueError("Invalid salt")
return _ffi.string(hashed)