Commits

Daniel Holth committed 86fcd22

use normal Python extension module instead of ctypes for bcrypt

Comments (0)

Files changed (7)

+0.5
+===
+- use normal Python extension module instead of ctypes for bcrypt
+
 0.4
 ===
 - don't import ez_setup

cryptacular/__init__.py

-import pkg_resources
-pkg_resources.declare_namespace(__name__)
+import pkg_resources # pragma NO COVERAGE
+pkg_resources.declare_namespace(__name__) # pragma NO COVERAGE

cryptacular/bcrypt/__init__.py

 
 import os
 import re
-from ctypes import cdll
-from ctypes import c_char_p, c_void_p, c_int, c_ulong, create_string_buffer
 
-_bcrypt = cdll.LoadLibrary(
-        os.path.join(os.path.dirname(__file__), '_bcrypt.so')
-        )
-
-# char *crypt_rn(const char *key, const char *setting, void *data, int size);
-_bcrypt.crypt_rn.restype = c_char_p
-_bcrypt.crypt_rn.argtypes = [c_char_p, c_char_p, c_void_p, c_int]
-
-# char *crypt_gensalt_rn(const char * prefix, unsigned long count,
-#                        const char *input, int size,
-#                        char *output, int output_size);
-_bcrypt.crypt_gensalt_rn.restype = c_char_p
-_bcrypt.crypt_gensalt_rn.argtypes = [c_char_p, c_ulong, c_char_p, c_int, c_char_p, c_int]
+from cryptacular.bcrypt._bcrypt import crypt_rn, crypt_gensalt_rn
 
 class BCRYPTPasswordManager(object):
 
 
         Note: only the first 72 characters of password are significant.
         """
-        settings = create_string_buffer(30)
-        data = create_string_buffer(61)
-        salt = os.urandom(16)
-        rc = _bcrypt.crypt_gensalt_rn('$2a$', 10, salt, len(salt), settings, len(settings))
-        if rc is None:
+        settings = crypt_gensalt_rn('$2a$', 10, 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 = _bcrypt.crypt_rn(password, settings, data, len(data))
+        rc = crypt_rn(password, settings)
         if rc is None: 
             raise ValueError("_bcrypt.crypt_rn returned None") # pragma NO COVERAGE
         return rc
             raise TypeError("encoded must be a str")
         if not self.match(encoded):
             return False
-        data = create_string_buffer(61)
-        rc = _bcrypt.crypt_rn(password, encoded, data, len(data))
+        rc = crypt_rn(password, encoded)
         if rc is None:
             raise ValueError("_bcrypt.crypt_rn returned None")
         return rc == encoded

cryptacular/bcrypt/_bcrypt.c

+/* Python extension module for bcrypt2.
+ *
+ * Daniel Holth <dholth@fastmail.fm>, 2010
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * 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
+ * furnished to do so, subject to the following conditions:
+ *
+ * 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
+ * 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
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <Python.h>
+#include "ow-crypt.h"
+
+static PyObject *_py_crypt_rn(PyObject *self, PyObject *args) {
+	char *rc;
+	const char *key;
+	const char *setting;
+	char output[61];
+
+	memset(output, 0, sizeof(output));
+
+	if (!PyArg_ParseTuple(args, "ss", &key, &setting)) {
+		return NULL;
+	}
+
+	Py_BEGIN_ALLOW_THREADS;
+	
+	/* key, setting, output, size */
+	rc = crypt_rn(key, setting, output, sizeof(output));
+
+	Py_END_ALLOW_THREADS;
+
+	if (rc == NULL) {
+		Py_RETURN_NONE;
+	}
+
+	output[sizeof(output) - 1] = '\0';
+
+	return Py_BuildValue("s", output);
+}
+
+static PyObject *_py_crypt_gensalt_rn(PyObject *self, PyObject *args) {
+	char *rc;
+	const char *prefix;
+	const int count;
+	const char *salt;
+	const Py_ssize_t salt_len;
+	char output[30];
+
+	memset(output, 0, sizeof(output));
+	
+	if (!PyArg_ParseTuple(args, "sis#", &prefix, &count, &salt, &salt_len)) {
+		return NULL;
+	}
+
+	/* prefix, count, input, size, output, output_size */
+	rc = crypt_gensalt_rn(prefix, count, salt, salt_len, output, sizeof(output));
+
+	if (rc == NULL) {
+		Py_RETURN_NONE;
+	}
+
+	output[sizeof(output) - 1] = '\0';
+
+	return Py_BuildValue("s", output);
+}
+
+static PyMethodDef BcryptMethods[] = {
+	{"crypt_rn", _py_crypt_rn, METH_VARARGS, "Encrypt password"},
+	{"crypt_gensalt_rn", _py_crypt_gensalt_rn, METH_VARARGS, "Generate salt"},
+	{NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC
+init_bcrypt(void)
+{
+	(void) Py_InitModule("_bcrypt", BcryptMethods);
+}
+

cryptacular/core/__init__.py

                     setter(self.preferred.encode(password))
                 return valid
 
-        raise ValueError("No configured password manager for 'encoded'")
+        raise ValueError("No configured password manager for given hash.")
 
     def match(self, encoded):
         return True in [m.match(encoded) for m in self._managers]

cryptacular/pbkdf2/__init__.py

 import os
 from base64 import urlsafe_b64encode, urlsafe_b64decode
 
-try:
+try: # pragma NO COVERAGE
     import M2Crypto.EVP
     _pbkdf2 = M2Crypto.EVP.pbkdf2
 except (ImportError, AttributeError): # pragma NO COVERAGE
 requires = [ ]
 
 setup(name='cryptacular',
-      version='0.4',
+      version='0.5',
       description='A password hashing framework with bcrypt and pbkdf2.',
       long_description=README + '\n\n' +  CHANGES,
       classifiers=[
               'crypt_blowfish-1.0.3/crypt_blowfish.c',
               'crypt_blowfish-1.0.3/crypt_gensalt.c',
               'crypt_blowfish-1.0.3/wrapper.c',
+              'cryptacular/bcrypt/_bcrypt.c',
               # how do I compile .S with distutils?
               # 'crypt_blowfish-1.0.3/x86.S', 
               ],
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.