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
|
||||
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):
|
||||
|
||||
|
@ -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 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.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 import utils, local
|
||||
from isso.utils import http, parse, JSONResponse as JSON
|
||||
from isso.utils.crypto import pbkdf2
|
||||
from isso.views import requires
|
||||
|
||||
# from Django appearently, looks good to me *duck*
|
||||
@ -139,6 +139,11 @@ class API(object):
|
||||
|
||||
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
|
||||
@requires(str, 'uri')
|
||||
def new(self, environ, request, uri):
|
||||
@ -197,7 +202,7 @@ class API(object):
|
||||
max_age=self.conf.getint('max-age'))
|
||||
|
||||
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'])
|
||||
|
||||
@ -429,7 +434,7 @@ class API(object):
|
||||
val = self.cache.get('hash', key.encode('utf-8'))
|
||||
|
||||
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)
|
||||
|
||||
item['hash'] = val
|
||||
|
Loading…
Reference in New Issue
Block a user