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

Add a KDF password manager. #14

Merged
merged 2 commits into from
Aug 31, 2017
Merged

Add a KDF password manager. #14

merged 2 commits into from
Aug 31, 2017

Conversation

jamadden
Copy link
Member

Fixes #9

Also refactor some of the internals of password.py to remove accumulated duplication in match() methods and encoding.

Fixes #9

Also refactor some of the internals of password.py to remove
accumulated duplication in match() methods and encoding.
Copy link
Member

@mgedmin mgedmin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM.

return (
encoded_password.startswith(b'{SHA}') or
encoded_password.startswith(self._prefix) or
encoded_password.startswith(b'{SHA1}'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could shorten this to a single .startswith((self._prefix, b'{SHA1}')) call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I always forget that. Possibly because if you leave off the extra set of parentheses, bad things happen.

"""
BCRYPT KDF password manager.

This manager converts a plain text password into a byte array .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange space in front of the ..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. Thanks.

#: The number of rounds of hashing that should be applied.
#: The higher the number, the slower it is. It should be at least
#: 50.
rounds = 1<<10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if rounds = 2**10 wouldn't be a bit more Pythonic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even just the integer 1024.

I just did what z3c.bcrypt did, although I reduced the rounds a bit. The original rounds was taking something like four seconds on my newish-computer, which seems excessive.

# XXX: Do we need to use a timing-safe comparison function here?
# z3c.bcrypt does, and so does bcrypt.hashpw. But there is no such
# public function
return hashed_password == encoded_password
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmac.compare_digest() is such a function, but only available in Python starting with 3.3.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll add that to the comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'll just use it when available. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, hmac.compare_digest was actually backported to 2.7.7 in 2014. Do we need to care about supporting older point releases in a new version? I can add appropriate requires-python metadata to make sure a new release is not installed by older versions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to care about supporting older point releases in a new version?

Given that this is a security feature for a security-critical package, and that people who care about security are probably running 2.7.9+ anyway, I feel like it is going to be OK to require 2.7.7+ (a three-year-old release) in a new version. Especially since it adds new security-related features. And especially especially since the requires-python metadata generally means it won't get installed by accident.

I added this to the PR.

if not self.match(hashed_password):
return False
rounds, salt, key = hashed_password[len(self._prefix):].split(b'$')
rounds = int(rounds, 16)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worrying that it's easy to make this function crash if you provide corrupt hashes (e.g. wrong number of '$' signs or non-hex digits in the rounds portion).

I am not a crypto person and I don't know whether that's fine (hashes are probably not attacker-supplied?) or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to avoid false positives here, so a crash is better than that, I think. But the hashed password would not be supplied by the attacher, the clear_password is. The hashed password is stored by the system and previously generated by this object, if it's being used according to the contract of IMatchingPasswordManager.

- whitespace
- startswith can take a tuple
- Use hmac.compare_digest for timing-safe comparisons. Require Python
  2.7.7 or above. Add appropriate require_python metadata for this.
Copy link
Member

@mgedmin mgedmin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm

@jamadden jamadden merged commit 5229798 into master Aug 31, 2017
@jamadden jamadden deleted the issue9 branch August 31, 2017 16:14
@jamadden
Copy link
Member Author

Thanks!

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

Successfully merging this pull request may close these issues.

None yet

2 participants