diff --git a/.travis.yml b/.travis.yml index 507c99b..8d9554e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ language: python python: - - 2.6 - - 2.7 - + - "2.6" + - "2.7" + - "3.2" + - "3.3" install: - python setup.py install script: diff --git a/bcrypt/__init__.py b/bcrypt/__init__.py index d755736..d4f2b2d 100644 --- a/bcrypt/__init__.py +++ b/bcrypt/__init__.py @@ -23,7 +23,7 @@ """ import os -from _bcrypt import * +from bcrypt._bcrypt import * def gensalt(log_rounds = 12): """Generate a random text salt for use with hashpw(). "log_rounds" diff --git a/bcrypt/bcrypt_python.c b/bcrypt/bcrypt_python.c index 6aea090..a8e1490 100644 --- a/bcrypt/bcrypt_python.c +++ b/bcrypt/bcrypt_python.c @@ -13,9 +13,11 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - +#define PY_SSIZE_T_CLEAN #include "Python.h" +#define PYBCRYPT_VERSION "0.4" + #if defined(_WIN32) typedef unsigned __int8 u_int8_t; typedef unsigned __int16 u_int16_t; @@ -23,7 +25,6 @@ typedef unsigned __int32 u_int32_t; #define strdup _strdup /* strdup is not ANSI C. _strdup */ #endif -/* $Id$ */ /* Import */ int pybc_bcrypt(const char *, const char *, char *, size_t); @@ -39,7 +40,7 @@ bcrypt_encode_salt(PyObject *self, PyObject *args, PyObject *kw_args) { static char *keywords[] = { "csalt", "log_rounds", NULL }; char *csalt = NULL; - int csaltlen = -1; + Py_ssize_t csaltlen = -1; long log_rounds = -1; char ret[64]; @@ -54,8 +55,12 @@ bcrypt_encode_salt(PyObject *self, PyObject *args, PyObject *kw_args) PyErr_SetString(PyExc_ValueError, "Invalid number of rounds"); return NULL; } - encode_salt(ret, csalt, csaltlen, log_rounds); - return PyString_FromString(ret); + encode_salt(ret, (u_int8_t *) csalt, csaltlen, log_rounds); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(ret); +#else + return PyString_FromString(ret); +#endif } PyDoc_STRVAR(bcrypt_hashpw_doc, @@ -72,7 +77,7 @@ bcrypt_hashpw(PyObject *self, PyObject *args, PyObject *kw_args) int ret; char *password_copy; char *salt_copy; - if (!PyArg_ParseTupleAndKeywords(args, kw_args, "ss:hashpw", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kw_args, "ss:hashpw", keywords, &password, &salt)) return NULL; @@ -93,8 +98,11 @@ bcrypt_hashpw(PyObject *self, PyObject *args, PyObject *kw_args) PyErr_SetString(PyExc_ValueError, "Invalid salt"); return NULL; } - +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(hashed); +#else return PyString_FromString(hashed); +#endif } static PyMethodDef bcrypt_methods[] = { @@ -107,12 +115,38 @@ static PyMethodDef bcrypt_methods[] = { PyDoc_STRVAR(module_doc, "Internal module used by bcrypt.\n"); -PyMODINIT_FUNC -init_bcrypt(void) -{ - PyObject *m; - - m = Py_InitModule3("bcrypt._bcrypt", bcrypt_methods, module_doc); - PyModule_AddStringConstant(m, "__version__", "0.3.1"); -} - +#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef bcrypt_module = { + PyModuleDef_HEAD_INIT, + "bcrypt._bcrypt", /* m_name */ + module_doc, /* m_doc */ + -1, /* m_size */ + bcrypt_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ + }; + + PyMODINIT_FUNC + PyInit__bcrypt(void) + { + PyObject *m; + + m = PyModule_Create(&bcrypt_module); + PyModule_AddStringConstant(m, "__version__", PYBCRYPT_VERSION); + return m; + } + +#else + + + PyMODINIT_FUNC + init_bcrypt(void) + { + PyObject *m; + + m = Py_InitModule3("bcrypt._bcrypt", bcrypt_methods, module_doc); + PyModule_AddStringConstant(m, "__version__", PYBCRYPT_VERSION); + } +#endif diff --git a/test/test.py b/test/test.py index 525dd6c..63543d6 100644 --- a/test/test.py +++ b/test/test.py @@ -17,8 +17,13 @@ # $Id$ import bcrypt - import unittest +import sys + +PY3 = (sys.version_info >= (3,0)) + +def enc(s): + return s.encode("latin-1") if PY3 else s test_vectors = [ [ '', '$2a$06$DCq7YPn5Rq63x1Lad4cll.', @@ -61,8 +66,20 @@ '$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS' ], [ '~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$12$WApznUOJfkEGSmYRfnkrPO', '$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC' ], + # [ enc('\xa3'), '$2a$05$CCCCCCCCCCCCCCCCCCCCC.', # latin-1 POUND SIGN + # '$2a$05$CCCCCCCCCCCCCCCCCCCCC.BvtRGGx3p8o0C5C36uS442Qqnrwofrq' ], + # [ enc('\xc2\xa3'), '$2a$05$CCCCCCCCCCCCCCCCCCCCC.', # utf-8 POUND SIGN + # '$2a$05$CCCCCCCCCCCCCCCCCCCCC.CAzSxlf0FLW7g1A5q7W/ZCj1xsN6A.e' ], ] + +if PY3: + # add 8-bit unicode test as well; to verify PY3 encodes it as UTF-8. + test_vectors.append( + [ '\u00A3', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.', # unicode POUND SIGN + '$2a$05$CCCCCCCCCCCCCCCCCCCCC.CAzSxlf0FLW7g1A5q7W/ZCj1xsN6A.e' ], + ) + class TestRadix(unittest.TestCase): def test_00__test_vectors(self): for plain, salt, expected in test_vectors: