Frank Smit avatar Frank Smit committed 86b8972

cryptacular.bcrypt should support Python 3 now.

Comments (0)

Files changed (4)

-syntax:glob
-*~
-.*
-*.pyc
-*.o
-*.so
-*.egg-info/*
-_build/*
-build/*
-dist/*
+syntax: regexp
+
+\.hgignore
+^py2-env/
+^py3-env/
+^build/
+^cryptacular\.egg-info/
+^dist/
+.*?\.pyc

cryptacular/bcrypt/__init__.py

 # Copyright (c) 2009 Daniel Holth <dholth@fastmail.fm>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
+# of this software and associated documentation files (the 'Software'), to deal
 # in the Software without restriction, including without limitation the rights
 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 # copies of the Software, and to permit persons to whom the Software is
 # The above copyright notice and this permission notice shall be included in
 # all copies or substantial portions of the Software.
 #
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
 __all__ = ['BCRYPTPasswordManager']
 
+
 import os
 import re
 
 from cryptacular.bcrypt._bcrypt import crypt_rn, crypt_gensalt_rn
-import cryptacular.core
+from cryptacular.core import _cmp, check_unicode
+
 
 class BCRYPTPasswordManager(object):
 
-    SCHEME = "BCRYPT"
-    PREFIX = "$2a$"
+    SCHEME = 'BCRYPT'
+    PREFIX = '$2a$'
     ROUNDS = 10
 
     _bcrypt_syntax = re.compile('\$2a\$[0-9]{2}\$[./A-Za-z0-9]{53}')
 
     def encode(self, password, rounds=None):
-        """Hash a password using bcrypt.
+        '''Hash a password using bcrypt.
 
         Note: only the first 72 characters of password are significant.
-        """
-        work_factor = rounds or self.ROUNDS
-        settings = crypt_gensalt_rn('$2a$', work_factor, os.urandom(16))
+        '''
+        rounds = rounds or self._rounds
+        settings = crypt_gensalt_rn(self._prefix, rounds, os.urandom(16))
         if settings is None:
-            raise ValueError("_bcrypt.crypt_gensalt_rn returned None") # pragma NO COVERAGE
-        if isinstance(password, unicode):
-            password = password.encode('utf-8')
-        if not isinstance(password, str):
-            raise TypeError("password must be a str")
-        rc = crypt_rn(password, settings)
-        if rc is None:
-            raise ValueError("_bcrypt.crypt_rn returned None") # pragma NO COVERAGE
-        return rc
+            raise ValueError('_bcrypt.crypt_gensalt_rn returned None')
+
+        encoded = crypt_rn(check_unicode(text), settings)
+        if encoded is None:
+            raise ValueError('_bcrypt.crypt_rn returned None')
+
+        return encoded
 
     def check(self, encoded, password):
-        """Check a bcrypt password hash against a password."""
-        if isinstance(password, unicode):
-            password = password.encode('utf-8')
-        if isinstance(encoded, unicode):
-            encoded = encoded.encode('utf-8')
-        if not isinstance(password, str):
-            raise TypeError("password must be a str")
-        if not isinstance(encoded, str):
-            raise TypeError("encoded must be a str")
-        if not self.match(encoded):
+        '''Check a bcrypt password hash against a password.
+        '''
+        if not self.valid(encoded):
             return False
-        rc = crypt_rn(password, encoded)
-        if rc is None:
-            raise ValueError("_bcrypt.crypt_rn returned None")
-        return cryptacular.core._cmp(rc, encoded)
+
+        encoded_text = crypt_rn(check_unicode(text), encoded)
+        if encoded_text is None:
+            raise ValueError('_bcrypt.crypt_rn returned None')
+
+        return _cmp(encoded_text, check_unicode(encoded))
 
     def match(self, hash):
-        """Return True if hash looks like a BCRYPT password hash."""
-        return self._bcrypt_syntax.match(hash) is not None
+        '''Return True if hash looks like a BCRYPT password hash.
+        '''
+        return self._bcrypt_syntax.match(encoded) is not None
 

cryptacular/bcrypt/_bcrypt.c

 /* Python extension module for bcrypt2.
  *
  * Daniel Holth <dholth@fastmail.fm>, 2010
+ * Frank Smit <frank@61924.nl>, 2011 (added Python 3 support)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * THE SOFTWARE.
  */
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include "ow-crypt.h"
 
 
-struct module_state {
-    PyObject *error;
-};
-
-
-#if PY_MAJOR_VERSION >= 3
-    #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
-#else
-    #define GETSTATE(m) (&_state)
-    static struct module_state _state;
-#endif
-
-
 static PyObject *
 _py_crypt_rn(PyObject *self, PyObject *args)
 {
         return NULL;
     }
 
+    Py_BEGIN_ALLOW_THREADS;
+
     /* prefix, count, input, size, output, output_size */
     rc = crypt_gensalt_rn(prefix, count, salt, salt_len, output, sizeof(output));
 
+    Py_END_ALLOW_THREADS;
+
     if (rc == NULL) {
         Py_RETURN_NONE;
     }
 
 
 #if PY_MAJOR_VERSION >= 3
-    static int
-    _bcrypt_traverse(PyObject *m, visitproc visit, void *arg)
-    {
-        Py_VISIT(GETSTATE(m)->error);
-        return 0;
-    }
-
-    static int
-    _bcrypt_clear(PyObject *m)
-    {
-        Py_CLEAR(GETSTATE(m)->error);
-        return 0;
-    }
-
     static struct PyModuleDef moduledef = {
         PyModuleDef_HEAD_INIT,
         "_bcrypt",
         NULL,
-        sizeof(struct module_state),
+        -1,
         _bcrypt_methods,
         NULL,
-        _bcrypt_traverse,
-        _bcrypt_clear,
+        NULL,
+        NULL,
         NULL
     };
 
         PyObject *module = Py_InitModule("_bcrypt", _bcrypt_methods);
     #endif
 
-    if (module == NULL) {
-        INITERROR;
-    }
-    struct module_state *st = GETSTATE(module);
-
-    st->error = PyErr_NewException("_bcrypt.Error", NULL, NULL);
-    if (st->error == NULL) {
-        Py_DECREF(module);
-        INITERROR;
-    }
-
     #if PY_MAJOR_VERSION >= 3
         return module;
     #endif
 }
-

cryptacular/core/__init__.py

 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
-__all__ = ['DelegatingPasswordManager', 'PasswordChecker', 'PasswordManager']
+__all__ = [
+    'DelegatingPasswordManager',
+    'PasswordChecker',
+    'PasswordManager',
+    'check_unicode'
+]
+
+
+if 'unicode' in __builtins__:
+    def check_unicode(text):
+        if isinstance(text, unicode):
+            text = text.encode('utf-8')
+        return text
+else:
+    def check_unicode(text):
+        return text
+
 
 class PasswordChecker(object):
 
 
         Most password schemes require encoded and password to be byte
         strings. The schemes included with this package convert unicode
-        'encoded' and 'password' to utf-8 as necessary."""
+        'encoded' and 'password' to utf-8 as necessary.
+        """
         raise NotImplementedError()
 
     def match(self, encoded):
         Most password schemes include a recognizable prefix in their hashes."""
         return encoded.startswith(self.PREFIX)
 
+
 class PasswordManager(PasswordChecker):
 
     def encode(self, password):
-        """Return hash of 'password' using this scheme."""
+        """Return hash of 'password' using this scheme.
+        """
         raise NotImplementedError()
 
+
 class DelegatingPasswordManager(object):
 
     SCHEME = None
 
 
 def _cmp(a, b):
-    """Constant-time comparison"""
+    """Constant-time comparison.
+    """
     if len(a) != len(b):
         return False
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.