Skip to content

Commit 6cc9f46

Browse files
committed
1.0.0
1 parent bdc451a commit 6cc9f46

File tree

12 files changed

+261
-0
lines changed

12 files changed

+261
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.mypy_cache

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Change Log
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
5+
and this project adheres to [Semantic Versioning](http://semver.org/).
6+
7+
## [1.0.0] - 2018-04-23
8+
### Added
9+
- initial release.

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include VERSION
2+
include README.md

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Python Encryption
2+
3+
This library provides three functions:
4+
5+
1. `generate_digest`
6+
2. `decrypt_digest`
7+
3. `validate_digest`
8+
9+
### generate_digest
10+
11+
This is a multi-arity function that will generate a digest using either a password-based key derivation function ([KDF](https://en.wikipedia.org/wiki/Key_derivation_function)) or a [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2) depending on the input given.
12+
13+
If a `password` argument is provided, then KDF will be used (along with a random salt) to generate a _non-deterministic_ digest.
14+
15+
If a `salt` is provided, then a PBKDF2 will be used to generate a _deterministic_ digest.
16+
17+
> Note: salts should be a minimum of 128bits (~16 characters) in length.
18+
19+
### KDF or PBKDF2 ?
20+
21+
It's recommended you use KDF (i.e. provide a `password` argument to the `generate_digest` function), this is because they're designed to be more computationally intensive than standard hashing functions and so are harder to use dictionary or rainbow table style attacks (as they would require a lot of extra memory resources and become more unfeasible as an attack vector).
22+
23+
By default the KDF will have a maximum computational time of `0.5`, but this can be overridden using the `maxtime` argument.
24+
25+
A PBKDF2 is able to provide deterministic output (and the ability to specify an explicit salt value). The internal implementation will repeat its process multiple times, thus reducing the feasibility of automated password cracking attempts.
26+
27+
> Note: when specifying a maxtime with `generate_digest`, ensure you include that same value when decrypting with `decrypt_digest` or validating via `validate_digest`.
28+
29+
### decrypt_digest and validate_digest
30+
31+
The `decrypt_digest` and `validate_digest` functions only apply to digests that have been generated using a password (i.e. KDF). Given the right password `decrypt_digest` will return the original message, and thus is considered more a form of symmetrical encryption than a straight one-way hash function. The `validate_digest` function will return a boolean true or false if the given password was able to decrypt the message.
32+
33+
## Usage
34+
35+
See the [tests](tests/test_interface.py) for examples of how to use these functions.
36+
37+
## Tests
38+
39+
See [tests](tests/test_interface.py).
40+
41+
## Dependencies
42+
43+
We have two dependency files:
44+
45+
* `requirements.txt`
46+
* `requirements-to-freeze.txt`
47+
48+
The latter should contain both 'explicit' versions (i.e. versions of dependencies our service is known to support) and 'non-explicit' versions (e.g. no versions defined), where as the former (`requirements.txt`) simply acts as a lockfile.
49+
50+
If we execute `pip freeze` the output will include dependencies that have the explicit versions we requested and the _latest_ version for those dependencies where we defined no explicit version. We can direct that output to a new file called `requirements.txt`.

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

requirements-to-freeze.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
scrypt>=0.8.6
2+
3+
# dev requirements
4+
flake8
5+
mock
6+
pytest
7+
pytest-cov

requirements.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
scrypt==0.8.6
2+
3+
# dev requirements
4+
flake8==3.5.0
5+
mock==2.0.0
6+
pytest==3.5.1
7+
pytest-cov==2.5.1
8+
## The following requirements were added by pip freeze:
9+
attrs==17.4.0
10+
coverage==4.5.1
11+
mccabe==0.6.1
12+
more-itertools==4.1.0
13+
pbr==4.0.2
14+
pluggy==0.6.0
15+
py==1.5.3
16+
pycodestyle==2.3.1
17+
pyflakes==1.6.0
18+
six==1.11.0

secure/__init__.py

Whitespace-only changes.

secure/interface.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import scrypt
2+
3+
from typing import Union
4+
5+
6+
class ArgumentError(Exception):
7+
pass
8+
9+
10+
def generate_digest(message: str,
11+
password: str = None,
12+
maxtime: Union[float, int] = 0.5,
13+
salt: str = "",
14+
length: int = 64) -> bytes:
15+
"""Multi-arity function for generating a digest.
16+
17+
Use KDF symmetric encryption given a password.
18+
Use deterministic hash function given a salt (or lack of password).
19+
"""
20+
21+
if password and salt:
22+
raise ArgumentError("only provide a password or a salt, not both")
23+
24+
if salt != "" and len(salt) < 16:
25+
raise ArgumentError("salts need to be minimum of 128bits (~16 characters)")
26+
27+
if password:
28+
return scrypt.encrypt(message, password, maxtime=maxtime)
29+
else:
30+
return scrypt.hash(message, salt, buflen=length)
31+
32+
33+
def decrypt_digest(digest: bytes,
34+
password: str,
35+
maxtime: Union[float, int] = 0.5) -> bytes:
36+
"""Decrypts digest using given password."""
37+
38+
return scrypt.decrypt(digest, password, maxtime)
39+
40+
41+
def validate_digest(digest: bytes,
42+
password: str,
43+
maxtime: Union[float, int] = 0.5) -> bool:
44+
"""Validate digest using given password."""
45+
46+
try:
47+
scrypt.decrypt(digest, password, maxtime)
48+
return True
49+
except scrypt.error:
50+
return False

setup.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python
2+
3+
from os import path
4+
from setuptools import setup, find_packages
5+
6+
BASE_DIR = path.abspath(path.dirname(__file__))
7+
8+
9+
def read(f):
10+
with open(path.join(BASE_DIR, f)) as fh:
11+
return fh.read()
12+
13+
14+
def get_version():
15+
version = read('VERSION').strip()
16+
if not version:
17+
raise RuntimeError('Cannot find version information')
18+
return version
19+
20+
21+
install_requires = []
22+
23+
setup(
24+
name='secure',
25+
version=get_version(),
26+
description='Provides standard interface for hashing, encrypting, decrypting and verifying user input',
27+
long_description=read('README.md'),
28+
author='Integralist',
29+
url='https://github.com/integralist/Python-Encryption',
30+
packages=find_packages(),
31+
install_requires=install_requires,
32+
keywords='integralist hash hashing encryption decryption library scrypt'
33+
)

0 commit comments

Comments
 (0)