remove Django's PBKDF2 in favour of werkzeug.security.pbkdf2_hex
This commit is contained in:
parent
932274921c
commit
211f637569
@ -375,6 +375,9 @@ class TestComments(unittest.TestCase):
|
|||||||
# just for the record
|
# just for the record
|
||||||
self.assertEqual(self.post('/id/1/dislike', content_type=js).status_code, 200)
|
self.assertEqual(self.post('/id/1/dislike', content_type=js).status_code, 200)
|
||||||
|
|
||||||
|
def testPBKDF2(self):
|
||||||
|
self.assertEqual(comments.API.pbkdf2(u"", Isso.salt, 1000, 6), u"42476aafe2e4")
|
||||||
|
|
||||||
|
|
||||||
class TestModeratedComments(unittest.TestCase):
|
class TestModeratedComments(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright (c) Django Software Foundation and individual contributors.
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
# are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in the
|
|
||||||
# documentation and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# 3. Neither the name of Django nor the names of its contributors may be used
|
|
||||||
# to endorse or promote products derived from this software without
|
|
||||||
# specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
import hmac
|
|
||||||
import struct
|
|
||||||
import base64
|
|
||||||
import hashlib
|
|
||||||
import binascii
|
|
||||||
import operator
|
|
||||||
|
|
||||||
from functools import reduce
|
|
||||||
|
|
||||||
|
|
||||||
def _bin_to_long(x):
|
|
||||||
"""
|
|
||||||
Convert a binary string into a long integer
|
|
||||||
|
|
||||||
This is a clever optimization for fast xor vector math
|
|
||||||
"""
|
|
||||||
return int(binascii.hexlify(x), 16)
|
|
||||||
|
|
||||||
|
|
||||||
def _long_to_bin(x, hex_format_string):
|
|
||||||
"""
|
|
||||||
Convert a long integer into a binary string.
|
|
||||||
hex_format_string is like "%020x" for padding 10 characters.
|
|
||||||
"""
|
|
||||||
return binascii.unhexlify((hex_format_string % x).encode('ascii'))
|
|
||||||
|
|
||||||
|
|
||||||
def _fast_hmac(key, msg, digest):
|
|
||||||
"""
|
|
||||||
A trimmed down version of Python's HMAC implementation.
|
|
||||||
|
|
||||||
This function operates on bytes.
|
|
||||||
"""
|
|
||||||
dig1, dig2 = digest(), digest()
|
|
||||||
if len(key) > dig1.block_size:
|
|
||||||
key = digest(key).digest()
|
|
||||||
key += b'\x00' * (dig1.block_size - len(key))
|
|
||||||
dig1.update(key.translate(hmac.trans_36))
|
|
||||||
dig1.update(msg)
|
|
||||||
dig2.update(key.translate(hmac.trans_5C))
|
|
||||||
dig2.update(dig1.digest())
|
|
||||||
return dig2
|
|
||||||
|
|
||||||
|
|
||||||
def _pbkdf2(password, salt, iterations, dklen=0, digest=None):
|
|
||||||
"""
|
|
||||||
Implements PBKDF2 as defined in RFC 2898, section 5.2
|
|
||||||
|
|
||||||
HMAC+SHA256 is used as the default pseudo random function.
|
|
||||||
|
|
||||||
Right now 10,000 iterations is the recommended default which takes
|
|
||||||
100ms on a 2.2Ghz Core 2 Duo. This is probably the bare minimum
|
|
||||||
for security given 1000 iterations was recommended in 2001. This
|
|
||||||
code is very well optimized for CPython and is only four times
|
|
||||||
slower than openssl's implementation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert iterations > 0
|
|
||||||
if not digest:
|
|
||||||
digest = hashlib.sha1
|
|
||||||
password = b'' + password
|
|
||||||
salt = b'' + salt
|
|
||||||
hlen = digest().digest_size
|
|
||||||
if not dklen:
|
|
||||||
dklen = hlen
|
|
||||||
if dklen > (2 ** 32 - 1) * hlen:
|
|
||||||
raise OverflowError('dklen too big')
|
|
||||||
l = -(-dklen // hlen)
|
|
||||||
r = dklen - (l - 1) * hlen
|
|
||||||
|
|
||||||
hex_format_string = "%%0%ix" % (hlen * 2)
|
|
||||||
|
|
||||||
def F(i):
|
|
||||||
def U():
|
|
||||||
u = salt + struct.pack(b'>I', i)
|
|
||||||
for j in range(int(iterations)):
|
|
||||||
u = _fast_hmac(password, u, digest).digest()
|
|
||||||
yield _bin_to_long(u)
|
|
||||||
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)
|
|
||||||
|
|
||||||
T = [F(x) for x in range(1, l + 1)]
|
|
||||||
return b''.join(T[:-1]) + T[-1][:r]
|
|
||||||
|
|
||||||
pbkdf2 = lambda text, salt, iterations, dklen: base64.b16encode(
|
|
||||||
_pbkdf2(text.encode('utf-8'), salt, iterations, dklen)).lower()
|
|
@ -9,17 +9,17 @@ import functools
|
|||||||
from itsdangerous import SignatureExpired, BadSignature
|
from itsdangerous import SignatureExpired, BadSignature
|
||||||
|
|
||||||
from werkzeug.http import dump_cookie
|
from werkzeug.http import dump_cookie
|
||||||
from werkzeug.routing import Rule
|
|
||||||
from werkzeug.wrappers import Response
|
|
||||||
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
|
|
||||||
from werkzeug.wsgi import get_current_url
|
from werkzeug.wsgi import get_current_url
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
|
from werkzeug.routing import Rule
|
||||||
|
from werkzeug.security import pbkdf2_hex
|
||||||
|
from werkzeug.wrappers import Response
|
||||||
|
from werkzeug.exceptions import BadRequest, Forbidden, NotFound
|
||||||
|
|
||||||
from isso.compat import text_type as str
|
from isso.compat import text_type as str
|
||||||
|
|
||||||
from isso import utils, local
|
from isso import utils, local
|
||||||
from isso.utils import http, parse, JSONResponse as JSON
|
from isso.utils import http, parse, JSONResponse as JSON
|
||||||
from isso.utils.crypto import pbkdf2
|
|
||||||
from isso.views import requires
|
from isso.views import requires
|
||||||
|
|
||||||
# from Django appearently, looks good to me *duck*
|
# from Django appearently, looks good to me *duck*
|
||||||
@ -139,6 +139,11 @@ class API(object):
|
|||||||
|
|
||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def pbkdf2(cls, text, salt, iterations, dklen):
|
||||||
|
# werkzeug.security.pbkdf2_hex returns always the native string type
|
||||||
|
return pbkdf2_hex(text.encode("utf-8"), salt, iterations, dklen)
|
||||||
|
|
||||||
@xhr
|
@xhr
|
||||||
@requires(str, 'uri')
|
@requires(str, 'uri')
|
||||||
def new(self, environ, request, uri):
|
def new(self, environ, request, uri):
|
||||||
@ -197,7 +202,7 @@ class API(object):
|
|||||||
max_age=self.conf.getint('max-age'))
|
max_age=self.conf.getint('max-age'))
|
||||||
|
|
||||||
rv["text"] = self.isso.render(rv["text"])
|
rv["text"] = self.isso.render(rv["text"])
|
||||||
rv["hash"] = pbkdf2(rv['email'] or rv['remote_addr'], self.isso.salt, 1000, 6).decode("utf-8")
|
rv["hash"] = API.pbkdf2(rv['email'] or rv['remote_addr'], self.isso.salt, 1000, 6)
|
||||||
|
|
||||||
self.cache.set('hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash'])
|
self.cache.set('hash', (rv['email'] or rv['remote_addr']).encode('utf-8'), rv['hash'])
|
||||||
|
|
||||||
@ -429,7 +434,7 @@ class API(object):
|
|||||||
val = self.cache.get('hash', key.encode('utf-8'))
|
val = self.cache.get('hash', key.encode('utf-8'))
|
||||||
|
|
||||||
if val is None:
|
if val is None:
|
||||||
val = pbkdf2(key, self.isso.salt, 1000, 6).decode("utf-8")
|
val = API.pbkdf2(key, self.isso.salt, 1000, 6)
|
||||||
self.cache.set('hash', key.encode('utf-8'), val)
|
self.cache.set('hash', key.encode('utf-8'), val)
|
||||||
|
|
||||||
item['hash'] = val
|
item['hash'] = val
|
||||||
|
Loading…
Reference in New Issue
Block a user