Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python3 port #8

Open
Tcll opened this issue Feb 21, 2020 · 2 comments
Open

python3 port #8

Tcll opened this issue Feb 21, 2020 · 2 comments

Comments

@Tcll
Copy link

Tcll commented Feb 21, 2020

if I could submit a PR, I would

  • the machine I wrote the updated code on is offline behind a non-functional subnet.
  • I'm not willing to give Micro$**t the undeserved pleasure of me putting my sources on their platform for them to control by forking a repo, I pulled my sources when they partnered, and I intend to keep things that way.
    (call me rude all you like, I'm not the one restricting freedoms and making a profit off my sheep)

but anyways, with that out of the way, here's the updated code:

import hmac
import hashlib
from struct import Struct
from operator import xor
from itertools import starmap

try:
    from itertools import izip as zip
except ImportError: pass # py3

try: range = xrange
except NameError: pass # py3

_pack_int = Struct('>I').pack


def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None):
    """Like :func:`pbkdf2_bin` but returns a hex encoded string."""
    return ''.join('%02x'%(v if type(v) is int else ord(v)) for v in
        pbkdf2_bin(data, salt, iterations, keylen, hashfunc))


def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
    """Returns a binary digest for the PBKDF2 hash algorithm of `data`
    with the given `salt`.  It iterates `iterations` time and produces a
    key of `keylen` bytes.  By default SHA-1 is used as hash function,
    a different hashlib `hashfunc` can be provided.
    """
    hashfunc = hashfunc or hashlib.sha1
    mac = hmac.new(data, None, hashfunc)

    buf = []
    for block in range(1, -(-keylen // mac.digest_size) + 1):
        h = mac.copy()
        h.update(salt + _pack_int(block))
        u = h.digest()
        rv = list(bytearray(u)) # needs further testing on py2 and could possibly be more performant
        for i in range(iterations - 1):
            h = mac.copy()
            h.update(bytes(u))
            u = h.digest()
            rv = starmap(xor, zip(rv, list(bytearray(u))))

        buf.extend(rv)
    return ''.join(map(chr, buf))[:keylen]

I've even ported the tests over and manually verified every expected key with your keys
(it was easier working 2 keyboards for 12 keys than copying the tests over on a flash drive)
all tests have passed.

a few things you may notice:

  • removed _pseudorandom() to improve overhead performance while maintaining namespace security
  • yes I'm locally overriding zip and range for py2, since this reflects a py3 namespace
    (@triggeredpythonists: I wouldn't do this if the original functionality was actually needed, please remain calm)

respect:

  • +1 for not following the crowd by using ''%() over ''.format() ;)

tips/advice:

  • you might want to from __future__ import print_function for porting print to print() in test()->check()

keep the credit, I care more about security than I care about being the one who ported your code ;)

@mrdc
Copy link

mrdc commented Oct 14, 2020

@Tcll Thanks for porting it to Python3. There is a small issue which should be also fixed for Python 3:

 h.update(salt + _pack_int(block))
TypeError: can only concatenate str (not "bytes") to str
* you might want to `from __future__ import print_function` for porting `print` to `print()` in `test()`->`check()`

Python 3 complains - should be changed manually to print()

@Tcll
Copy link
Author

Tcll commented Oct 16, 2020

ah that can just be fixed by ensuring salt is a bytes object

if you don't want to sacrifice much performance for encodings:

assert type(salt) is bytes

yes it still throws an error, but at least now it's educational
thank you Struct and python 3 for giving us some extra tediosity.
(because 'a' can be any number of bytes in size thanks to utf-## or others)

Python 3 complains - should be changed manually to print()

I meant using that for Python 2
yes you still manually have to change it from the statement to the function
but it makes your code compatible across both

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants